Merge "Expand Gatekeeper test"
diff --git a/Android.bp b/Android.bp
index 00ba1e2..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,
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 5290a9a..69d25f4 100644
--- a/atrace/1.0/Android.bp
+++ b/atrace/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.atrace@1.0",
     root: "android.hardware",
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/audio/2.0/Android.bp b/audio/2.0/Android.bp
index 35f6803..2689355 100644
--- a/audio/2.0/Android.bp
+++ b/audio/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@2.0",
     root: "android.hardware",
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 4957a14..1a05d41 100644
--- a/audio/4.0/Android.bp
+++ b/audio/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.audio@4.0",
     root: "android.hardware",
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 365a654..525b0b9 100644
--- a/audio/5.0/Android.bp
+++ b/audio/5.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@5.0",
     root: "android.hardware",
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 d7880b6..09705e6 100644
--- a/audio/6.0/Android.bp
+++ b/audio/6.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@6.0",
     root: "android.hardware",
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
index d07ce12..5533df3 100644
--- a/audio/7.0/Android.bp
+++ b/audio/7.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@7.0",
     root: "android.hardware",
diff --git a/audio/7.0/IDevice.hal b/audio/7.0/IDevice.hal
index e423f29..85c789a 100644
--- a/audio/7.0/IDevice.hal
+++ b/audio/7.0/IDevice.hal
@@ -245,6 +245,7 @@
     /**
      * 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.
@@ -255,6 +256,7 @@
     /**
      * 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.
diff --git a/audio/7.0/IStream.hal b/audio/7.0/IStream.hal
index ab9aa7d..e4987c2 100644
--- a/audio/7.0/IStream.hal
+++ b/audio/7.0/IStream.hal
@@ -73,14 +73,14 @@
 
     /**
      * Sets stream parameters. Only sets parameters that are specified.
-     * See the description of AudioConfigBase for the details.
      *
      * Optional method. If implemented, only called on a stopped stream.
      *
      * @param config basic stream configuration.
      * @return retval operation completion status.
      */
-    setAudioProperties(AudioConfigBase config) generates (Result retval);
+    setAudioProperties(AudioConfigBaseOptional config)
+            generates (Result retval);
 
     /**
      * Applies audio effect to the stream.
@@ -110,6 +110,7 @@
 
     /**
      * Return the set of devices which this stream is connected to.
+     *
      * Optional method
      *
      * @return retval operation completion status: OK or NOT_SUPPORTED.
@@ -133,6 +134,7 @@
     /**
      * 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
diff --git a/audio/7.0/IStreamIn.hal b/audio/7.0/IStreamIn.hal
index bf9ae52..be4bda4 100644
--- a/audio/7.0/IStreamIn.hal
+++ b/audio/7.0/IStreamIn.hal
@@ -24,6 +24,7 @@
      * 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.
@@ -33,6 +34,7 @@
 
     /**
      * Set the input gain for the audio driver.
+     *
      * Optional method
      *
      * @param gain 1.0f is unity, 0.0f is zero.
@@ -42,6 +44,7 @@
 
     /**
      * 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.
@@ -148,7 +151,8 @@
 
     /**
      * Return a recent count of the number of audio frames received and the
-     * clock time associated with that frame count.
+     * 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,
diff --git a/audio/7.0/IStreamOut.hal b/audio/7.0/IStreamOut.hal
index 4daab26..359dce4 100644
--- a/audio/7.0/IStreamOut.hal
+++ b/audio/7.0/IStreamOut.hal
@@ -35,6 +35,7 @@
      * 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.
@@ -46,6 +47,7 @@
 
     /**
      * 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.
@@ -130,6 +132,7 @@
     /**
      * 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.
@@ -141,6 +144,7 @@
      * 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.
@@ -253,8 +257,11 @@
     drain(AudioDrain type) generates (Result retval);
 
     /**
-     * Notifies to the audio driver to flush the queued data. Stream must
-     * already be paused before calling 'flush'.
+     * 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.
@@ -266,12 +273,14 @@
     /**
      * 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 is not reset to zero when output enters
-     * standby. Also returns the value of CLOCK_MONOTONIC as of this
+     * 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
@@ -289,6 +298,7 @@
     /**
      * 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.
diff --git a/audio/7.0/IStreamOutEventCallback.hal b/audio/7.0/IStreamOutEventCallback.hal
index 4de767f..8ef0060 100644
--- a/audio/7.0/IStreamOutEventCallback.hal
+++ b/audio/7.0/IStreamOutEventCallback.hal
@@ -51,6 +51,25 @@
      * "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]
      *
diff --git a/audio/7.0/config/Android.bp b/audio/7.0/config/Android.bp
index f67cc7c..096ab6a 100644
--- a/audio/7.0/config/Android.bp
+++ b/audio/7.0/config/Android.bp
@@ -1,6 +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
index 1da8b09..c2585bd 100644
--- a/audio/7.0/config/api/current.txt
+++ b/audio/7.0/config/api/current.txt
@@ -45,6 +45,8 @@
     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;
@@ -104,6 +106,7 @@
     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;
@@ -138,6 +141,7 @@
     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;
@@ -155,6 +159,12 @@
     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;
@@ -198,10 +208,13 @@
     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;
@@ -211,6 +224,7 @@
     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;
@@ -222,6 +236,10 @@
     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;
@@ -250,7 +268,6 @@
     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_NONE;
     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;
@@ -263,7 +280,6 @@
     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_NONE;
     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;
@@ -483,10 +499,12 @@
   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>);
@@ -541,7 +559,7 @@
 
   public enum Version {
     method @NonNull public String getRawName();
-    enum_constant public static final android.audio.policy.configuration.V7_0.Version _1_0;
+    enum_constant public static final android.audio.policy.configuration.V7_0.Version _7_0;
   }
 
   public class Volume {
diff --git a/audio/7.0/config/audio_policy_configuration.xsd b/audio/7.0/config/audio_policy_configuration.xsd
index 56b3a27..e0df359 100644
--- a/audio/7.0/config/audio_policy_configuration.xsd
+++ b/audio/7.0/config/audio_policy_configuration.xsd
@@ -20,7 +20,7 @@
     <!-- List the config versions supported by audio policy. -->
     <xs:simpleType name="version">
         <xs:restriction base="xs:decimal">
-            <xs:enumeration value="1.0"/>
+            <xs:enumeration value="7.0"/>
         </xs:restriction>
     </xs:simpleType>
     <xs:simpleType name="halVersion">
@@ -159,13 +159,9 @@
         <xs:annotation>
             <xs:documentation xml:lang="en">
               The flags indicate suggested stream attributes supported by the profile.
-              Use of AUDIO_{INPUT|OUTPUT}_FLAG_NONE in the XML file isn't required
-              as empty flag lists are allowed. However these constants are useful for
-              representing an empty enum value.
             </xs:documentation>
         </xs:annotation>
         <xs:restriction base="xs:string">
-            <xs:enumeration value="AUDIO_OUTPUT_FLAG_NONE" />
             <xs:enumeration value="AUDIO_OUTPUT_FLAG_DIRECT" />
             <xs:enumeration value="AUDIO_OUTPUT_FLAG_PRIMARY" />
             <xs:enumeration value="AUDIO_OUTPUT_FLAG_FAST" />
@@ -181,7 +177,6 @@
             <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_NONE" />
             <xs:enumeration value="AUDIO_INPUT_FLAG_FAST" />
             <xs:enumeration value="AUDIO_INPUT_FLAG_HW_HOTWORD" />
             <xs:enumeration value="AUDIO_INPUT_FLAG_RAW" />
@@ -257,6 +252,7 @@
             <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"/>
@@ -308,6 +304,7 @@
             <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"/>
@@ -315,13 +312,17 @@
         </xs:restriction>
     </xs:simpleType>
     <xs:simpleType name="vendorExtension">
-        <!-- Vendor extension names must be prefixed by "VX_" to distinguish them from AOSP values.
-             Vendor are encouraged to namespace their module names to avoid conflicts.
-             Example for an hypothetical Google virtual reality device:
-                <devicePort tagName="VR" type="VX_GOOGLE_VR" role="sink">
+        <!-- Vendor extension names must be prefixed by "VX_" to distinguish them from
+             AOSP values. Vendors must namespace their names to avoid conflicts. The
+             namespace part must only use capital latin characters and decimal digits and
+             consist of at least 3 characters. The part of the extension name after the
+             namespace may in addition include underscores. Example for a hypothetical
+             Google virtual reality device:
+
+                 <devicePort tagName="VR" type="VX_GOOGLE_VR" role="sink" />
         -->
         <xs:restriction base="xs:string">
-            <xs:pattern value="VX_[_a-zA-Z0-9]+"/>
+            <xs:pattern value="VX_[A-Z0-9]{3,}_[_A-Z0-9]+"/>
         </xs:restriction>
     </xs:simpleType>
     <xs:simpleType name="extendableAudioDevice">
@@ -329,6 +330,7 @@
     </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"/>
@@ -406,6 +408,13 @@
             <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">
@@ -499,6 +508,8 @@
             <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"/>
@@ -545,11 +556,18 @@
     <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">
diff --git a/audio/7.0/config/update_audio_policy_config.sh b/audio/7.0/config/update_audio_policy_config.sh
index 051a0df..159fa35 100755
--- a/audio/7.0/config/update_audio_policy_config.sh
+++ b/audio/7.0/config/update_audio_policy_config.sh
@@ -113,6 +113,9 @@
 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
diff --git a/audio/README.md b/audio/README.md
index b77b9ba..1938ad4 100644
--- a/audio/README.md
+++ b/audio/README.md
@@ -7,47 +7,49 @@
 
 ## Directory Structure
 
-* `2.0` -- version 2.0 of the core HIDL API. Note that `.hal` files
+* `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
+   - `config` — the XSD schema for the Audio Policy Manager
      configuration file.
-* `4.0` -- version 4.0 of the core HIDL API.
+* `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.
+* `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
+   - `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
+   - `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
+      - `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,
+   - `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
+       - `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.
+* `effect` — same for the effect HIDL API.
    - `2.0`
-      - `config` -- the XSD schema for the Audio Effects configuration
-        file.
+      - `config` — the XSD schema for the Audio Effects configuration file.
    - `4.0`
    - ...
    - `all-versions`
-      - `default`
-      - `vts`
-* `policy` -- Configurable Audio Policy schemes.
-   - `1.0` -- note that versions of CAP are not linked to the 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.
+      - `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 dc4bca4..ea88b06 100644
--- a/audio/common/4.0/Android.bp
+++ b/audio/common/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.audio.common@4.0",
     root: "android.hardware",
diff --git a/audio/common/5.0/Android.bp b/audio/common/5.0/Android.bp
index bf265a5..c4f3d4c 100644
--- a/audio/common/5.0/Android.bp
+++ b/audio/common/5.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@5.0",
     root: "android.hardware",
diff --git a/audio/common/6.0/Android.bp b/audio/common/6.0/Android.bp
index caeee6f..fc54caf 100644
--- a/audio/common/6.0/Android.bp
+++ b/audio/common/6.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@6.0",
     root: "android.hardware",
diff --git a/audio/common/7.0/Android.bp b/audio/common/7.0/Android.bp
index 1c016b4..2f7665e 100644
--- a/audio/common/7.0/Android.bp
+++ b/audio/common/7.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@7.0",
     root: "android.hardware",
@@ -16,15 +25,14 @@
 cc_library {
     name: "android.hardware.audio.common@7.0-enums",
     vendor_available: true,
-    generated_headers: ["audio_policy_configuration_V7_0"],
-    generated_sources: ["audio_policy_configuration_V7_0"],
+    generated_headers: ["audio_policy_configuration_V7_0_enums"],
+    generated_sources: ["audio_policy_configuration_V7_0_enums"],
     header_libs: ["libxsdc-utils"],
-    export_generated_headers: ["audio_policy_configuration_V7_0"],
+    export_generated_headers: ["audio_policy_configuration_V7_0_enums"],
     export_header_lib_headers: ["libxsdc-utils"],
     export_include_dirs: ["enums/include"],
     shared_libs: [
         "libbase",
         "liblog",
-        "libxml2",
     ],
 }
diff --git a/audio/effect/all-versions/default/OWNERS b/audio/common/7.0/enums/OWNERS
similarity index 67%
rename from audio/effect/all-versions/default/OWNERS
rename to audio/common/7.0/enums/OWNERS
index 6fdc97c..24071af 100644
--- a/audio/effect/all-versions/default/OWNERS
+++ b/audio/common/7.0/enums/OWNERS
@@ -1,3 +1,2 @@
 elaurent@google.com
-krocard@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
index c0042db..a92a277 100644
--- a/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
+++ b/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0_ENUMS_H
-#define ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0_ENUMS_H
+#ifndef ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0__ENUMS_H
+#define ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0__ENUMS_H
 
 #include <sys/types.h>
-#include <algorithm>
-#include <cctype>
+#include <regex>
+#include <string>
 
-#include <android_audio_policy_configuration_V7_0.h>
+#include <android_audio_policy_configuration_V7_0_enums.h>
 
 namespace android::audio::policy::configuration::V7_0 {
 
@@ -94,6 +94,7 @@
         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:
@@ -116,6 +117,7 @@
             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:
@@ -155,6 +157,7 @@
         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:
@@ -197,6 +200,7 @@
         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:
@@ -212,12 +216,25 @@
     return isOutputDevice(stringToAudioDevice(device));
 }
 
-static inline bool isVendorExtension(const std::string& s) {
-    // Must match the "vendorExtension" rule from the XSD file.
+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 &&
-           std::all_of(s.begin() + vendorPrefix.size(), s.end(),
-                       [](unsigned char c) { return c == '_' || std::isalnum(c); });
+    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) {
@@ -256,6 +273,28 @@
     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
+#endif  // ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0__ENUMS_H
diff --git a/audio/common/7.0/example/Android.bp b/audio/common/7.0/example/Android.bp
index 03c1cd8..a85e4fa 100644
--- a/audio/common/7.0/example/Android.bp
+++ b/audio/common/7.0/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.audio@7.0-service.example",
     vendor: true,
@@ -35,7 +44,6 @@
         "libcutils",
         "libhidlbase",
         "liblog",
-        "libxml2",
         "libutils",
         "android.hardware.audio@7.0",
         "android.hardware.audio.common@7.0",
diff --git a/audio/common/7.0/example/Effect.cpp b/audio/common/7.0/example/Effect.cpp
index 27f28c6..0621669 100644
--- a/audio/common/7.0/example/Effect.cpp
+++ b/audio/common/7.0/example/Effect.cpp
@@ -17,7 +17,7 @@
 #define LOG_TAG "EffectsFactory7.0"
 #include <log/log.h>
 
-#include <android_audio_policy_configuration_V7_0.h>
+#include <android_audio_policy_configuration_V7_0-enums.h>
 
 #include "Effect.h"
 
@@ -107,14 +107,13 @@
 }
 
 Return<void> Effect::getConfig(getConfig_cb _hidl_cb) {
-    const EffectConfig config = {
-            {} /* inputCfg */,
-            // outputCfg
-            {{} /* buffer */,
-             {toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT), 48000 /* samplingRateHz */,
-              toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO)}, /* base */
-             EffectBufferAccess::ACCESS_ACCUMULATE,
-             0 /* mask */}};
+    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();
 }
diff --git a/audio/effect/all-versions/default/OWNERS b/audio/common/7.0/example/OWNERS
similarity index 67%
copy from audio/effect/all-versions/default/OWNERS
copy to audio/common/7.0/example/OWNERS
index 6fdc97c..24071af 100644
--- a/audio/effect/all-versions/default/OWNERS
+++ b/audio/common/7.0/example/OWNERS
@@ -1,3 +1,2 @@
 elaurent@google.com
-krocard@google.com
 mnaganov@google.com
diff --git a/audio/common/7.0/types.hal b/audio/common/7.0/types.hal
index ed6d94f..6fca93e 100644
--- a/audio/common/7.0/types.hal
+++ b/audio/common/7.0/types.hal
@@ -61,6 +61,8 @@
  * 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;
 
@@ -118,12 +120,39 @@
  * Base configuration attributes applicable to any stream of audio.
  */
 struct AudioConfigBase {
-    AudioFormat format;                 // empty means 'unspecified'
-    uint32_t sampleRateHz;              // 0 means 'unspecified'
-    AudioChannelMask channelMask;       // empty means 'unspecified'
+    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 {
@@ -135,6 +164,35 @@
 };
 
 /**
+ * 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.
  */
@@ -290,15 +348,15 @@
 struct PlaybackTrackMetadata {
     AudioUsage usage;
     AudioContentType contentType;
-    /** Tags from AudioTrack audio atttributes */
-    vec<AudioTag> tags;
-    AudioChannelMask channelMask;
     /**
      * 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. */
@@ -309,9 +367,6 @@
 /** Metadata of a record track for a StreamIn. */
 struct RecordTrackMetadata {
     AudioSource source;
-    /** Tags from AudioTrack audio atttributes */
-    vec<AudioTag> tags;
-    AudioChannelMask channelMask;
     /**
      * Positive linear gain applied to the track samples. 0 being muted and 1 is no attenuation,
      * 2 means double amplification...
@@ -325,6 +380,9 @@
         Monostate unspecified;
         DeviceAddress device;
     } destination;
+    AudioChannelMask channelMask;
+    /** Tags from AudioRecord audio atttributes */
+    vec<AudioTag> tags;
 };
 
 /** Metadatas of the sink of a StreamIn. */
@@ -439,11 +497,9 @@
      */
     AudioPortHandle id;
     /**
-     * Basic parameters: sampling rate, format, channel mask. Only some of the
-     * parameters (or none) may be set. See the documentation of the
-     * AudioConfigBase struct.
+     * Basic parameters: sampling rate, format, channel mask.
      */
-    AudioConfigBase base;
+    AudioConfigBaseOptional base;
     /** Associated gain control. */
     safe_union OptionalGain {
         Monostate unspecified;
@@ -469,8 +525,12 @@
      * E.g. "telephony_tx" or "fm_tuner".
      */
     string name;
-    /** List of audio profiles supported by the port. */
-    vec<AudioProfile> profiles;
+    /**
+     * 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. */
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/default/7.0/HidlUtils.cpp b/audio/common/all-versions/default/7.0/HidlUtils.cpp
index de19faf..5a5b5d2 100644
--- a/audio/common/all-versions/default/7.0/HidlUtils.cpp
+++ b/audio/common/all-versions/default/7.0/HidlUtils.cpp
@@ -16,6 +16,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <algorithm>
 
 #define LOG_TAG "HidlUtils"
 #include <log/log.h>
@@ -147,6 +148,59 @@
     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);
@@ -228,7 +282,7 @@
                                              hidl_vec<AudioGainMode>* gainModeMask) {
     status_t status = NO_ERROR;
     std::vector<AudioGainMode> result;
-    for (uint32_t bit = 0; bit < sizeof(audio_gain_mode_t) * 8; ++bit) {
+    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);
@@ -238,6 +292,7 @@
                 ALOGE("Unknown audio gain mode value 0x%X", flag);
                 status = BAD_VALUE;
             }
+            halGainModeMask = static_cast<audio_gain_mode_t>(halGainModeMask & ~flag);
         }
     }
     *gainModeMask = result;
@@ -280,25 +335,35 @@
     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) {
-    *streamType = audio_stream_type_to_string(halStreamType);
-    if (!streamType->empty() && !xsd::isUnknownAudioStreamType(*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;
     }
-    ALOGE("Unknown audio stream type value 0x%X", halStreamType);
-    return BAD_VALUE;
 }
 
 status_t HidlUtils::audioStreamTypeToHal(const AudioStreamType& streamType,
                                          audio_stream_type_t* halStreamType) {
-    if (!xsd::isUnknownAudioStreamType(streamType) &&
-        audio_stream_type_from_string(streamType.c_str(), 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;
     }
-    ALOGE("Unknown audio stream type \"%s\"", streamType.c_str());
-    *halStreamType = AUDIO_STREAM_DEFAULT;
-    return BAD_VALUE;
 }
 
 status_t HidlUtils::audioConfigFromHal(const audio_config_t& halConfig, bool isInput,
@@ -508,23 +573,14 @@
               audio_port_config_has_input_direction(&halConfig), isInput);
         result = BAD_VALUE;
     }
-    if (halConfig.config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
-        config->base.sampleRateHz = halConfig.sample_rate;
-    } else {
-        config->base.sampleRateHz = {};
-    }
-    if (halConfig.config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
-        CONVERT_CHECKED(
-                audioChannelMaskFromHal(halConfig.channel_mask, isInput, &config->base.channelMask),
-                result);
-    } else {
-        config->base.channelMask = {};
-    }
-    if (halConfig.config_mask & AUDIO_PORT_CONFIG_FORMAT) {
-        CONVERT_CHECKED(audioFormatFromHal(halConfig.format, &config->base.format), result);
-    } else {
-        config->base.format = {};
-    }
+    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()),
@@ -540,19 +596,23 @@
     status_t result = NO_ERROR;
     memset(halConfig, 0, sizeof(audio_port_config));
     halConfig->id = config.id;
-    halConfig->config_mask = {};
-    if (config.base.sampleRateHz != 0) {
+    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 = config.base.sampleRateHz;
+        halConfig->sample_rate = halConfigBase.sample_rate;
     }
-    if (!config.base.channelMask.empty()) {
+    if (channelMaskSpecified) {
         halConfig->config_mask |= AUDIO_PORT_CONFIG_CHANNEL_MASK;
-        CONVERT_CHECKED(audioChannelMaskToHal(config.base.channelMask, &halConfig->channel_mask),
-                        result);
+        halConfig->channel_mask = halConfigBase.channel_mask;
     }
-    if (!config.base.format.empty()) {
+    if (formatSpecified) {
         halConfig->config_mask |= AUDIO_PORT_CONFIG_FORMAT;
-        CONVERT_CHECKED(audioFormatToHal(config.base.format, &halConfig->format), result);
+        halConfig->format = halConfigBase.format;
     }
     if (config.gain.getDiscriminator() ==
         AudioPortConfig::OptionalGain::hidl_discriminator::config) {
@@ -655,6 +715,27 @@
     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);
@@ -698,11 +779,7 @@
     CONVERT_CHECKED(audioPortExtendedInfoFromHal(halPort.role, halPort.type, halDevice, halMix,
                                                  halSession, &port->ext, &isInput),
                     result);
-    port->profiles.resize(halPort.num_audio_profiles);
-    for (size_t i = 0; i < halPort.num_audio_profiles; ++i) {
-        CONVERT_CHECKED(audioProfileFromHal(halPort.audio_profiles[i], isInput, &port->profiles[i]),
-                        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);
@@ -720,15 +797,7 @@
         ALOGE("HIDL Audio Port name is too long: %zu", port.name.size());
         result = BAD_VALUE;
     }
-    halPort->num_audio_profiles = port.profiles.size();
-    if (halPort->num_audio_profiles > AUDIO_PORT_MAX_AUDIO_PROFILES) {
-        ALOGE("HIDL Audio Port has too many profiles: %u", halPort->num_audio_profiles);
-        halPort->num_audio_profiles = AUDIO_PORT_MAX_AUDIO_PROFILES;
-        result = BAD_VALUE;
-    }
-    for (size_t i = 0; i < halPort->num_audio_profiles; ++i) {
-        CONVERT_CHECKED(audioProfileToHal(port.profiles[i], &halPort->audio_profiles[i]), result);
-    }
+    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);
@@ -764,6 +833,110 @@
     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;
@@ -810,15 +983,17 @@
     return result;
 }
 
-status_t HidlUtils::audioTagsFromHal(const char* halTags, hidl_vec<AudioTag>* tags) {
-    std::vector<std::string> strTags = utils::splitString(halTags, sAudioTagSeparator);
+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) {
-        if (xsd::isVendorExtension(strTags[from])) {
-            (*tags)[to++] = strTags[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;
         }
     }
@@ -841,6 +1016,7 @@
             halTagsBuffer << tag;
             hasValue = true;
         } else {
+            ALOGE("Vendor extension tag is ill-formed: \"%s\"", tag.c_str());
             result = BAD_VALUE;
         }
     }
@@ -851,6 +1027,31 @@
     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;
diff --git a/audio/common/all-versions/default/Android.bp b/audio/common/all-versions/default/Android.bp
index 45f0b8f..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"],
@@ -114,7 +123,7 @@
     ],
 }
 
-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"],
@@ -140,7 +149,6 @@
         "android.hardware.audio.common@7.0",
         "android.hardware.audio.common@7.0-enums",
         "libbase",
-        "libxml2",
     ],
     cflags: [
         "-DMAJOR_VERSION=7",
@@ -152,6 +160,32 @@
 // 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"],
 
diff --git a/audio/common/all-versions/default/HidlUtils.h b/audio/common/all-versions/default/HidlUtils.h
index 8e9275c..98ecc07 100644
--- a/audio/common/all-versions/default/HidlUtils.h
+++ b/audio/common/all-versions/default/HidlUtils.h
@@ -20,6 +20,8 @@
 #include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include <system/audio.h>
 
@@ -89,6 +91,15 @@
                                            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);
@@ -109,8 +120,16 @@
                                            AudioStreamType* streamType);
     static status_t audioStreamTypeToHal(const AudioStreamType& streamType,
                                          audio_stream_type_t* halStreamType);
-    static status_t audioTagsFromHal(const char* halTags, hidl_vec<AudioTag>* tags);
+    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,
@@ -130,6 +149,10 @@
                                                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
 
@@ -195,6 +218,9 @@
                *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;
 }
@@ -234,6 +260,7 @@
         device->rSubmixAddress = halDeviceAddress;
         return OK;
     }
+    // Fall back to bus address for other device types, e.g. for microphones.
     device->busAddress = halDeviceAddress;
     return NO_ERROR;
 }
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
index 4316ccf..c965113 100644
--- a/audio/common/all-versions/default/TEST_MAPPING
+++ b/audio/common/all-versions/default/TEST_MAPPING
@@ -1,6 +1,9 @@
 {
   "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
index 85edc7b..6c4c94d 100644
--- a/audio/common/all-versions/default/UuidUtils.cpp
+++ b/audio/common/all-versions/default/UuidUtils.cpp
@@ -42,6 +42,14 @@
     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
diff --git a/audio/common/all-versions/default/UuidUtils.h b/audio/common/all-versions/default/UuidUtils.h
index 38db48a..cd04fb0 100644
--- a/audio/common/all-versions/default/UuidUtils.h
+++ b/audio/common/all-versions/default/UuidUtils.h
@@ -17,14 +17,14 @@
 #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>
 
-using ::android::hardware::hidl_vec;
-
 namespace android {
 namespace hardware {
 namespace audio {
@@ -38,6 +38,7 @@
   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
diff --git a/audio/common/all-versions/default/service/Android.bp b/audio/common/all-versions/default/service/Android.bp
index f163a2f..0d4775c6a 100644
--- a/audio/common/all-versions/default/service/Android.bp
+++ b/audio/common/all-versions/default/service/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.audio.service",
 
diff --git a/audio/common/all-versions/default/service/service.cpp b/audio/common/all-versions/default/service/service.cpp
index 710ddce..bbc14ad 100644
--- a/audio/common/all-versions/default/service/service.cpp
+++ b/audio/common/all-versions/default/service/service.cpp
@@ -63,17 +63,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",
         }
     };
 
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
index fef88b4..c9e6fac 100644
--- a/audio/common/all-versions/default/tests/hidlutils_tests.cpp
+++ b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
@@ -35,30 +35,31 @@
 using namespace ::android::audio::policy::configuration::V7_0;
 }
 
-static constexpr audio_channel_mask_t kInvalidHalChannelMask =
-        static_cast<audio_channel_mask_t>(0xFFFFFFFFU);
+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 = static_cast<audio_format_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);
-static constexpr audio_source_t kInvalidHalSource = static_cast<audio_source_t>(0xFFFFFFFFU);
-static constexpr audio_stream_type_t kInvalidHalStreamType =
-        static_cast<audio_stream_type_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(AUDIO_CHANNEL_INVALID,
-                                                            false /*isInput*/, &invalid));
-    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskFromHal(AUDIO_CHANNEL_INVALID, true /*isInput*/,
-                                                            &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));
@@ -148,40 +149,241 @@
     }
 }
 
+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;
-    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseFromHal({.sample_rate = 0,
-                                                            .channel_mask = kInvalidHalChannelMask,
-                                                            .format = kInvalidHalFormat},
-                                                           false /*isInput*/, &invalid));
-    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseFromHal({.sample_rate = 0,
-                                                            .channel_mask = kInvalidHalChannelMask,
-                                                            .format = kInvalidHalFormat},
-                                                           true /*isInput*/, &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;
-    invalid.sampleRateHz = 0;
-    invalid.channelMask = "random string";
-    invalid.format = "random string";
-    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseToHal(invalid, &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 configBase;
-    configBase.sampleRateHz = 44100;
-    configBase.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
-    configBase.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
-    audio_config_base_t halConfigBase;
-    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseToHal(configBase, &halConfigBase));
-    AudioConfigBase configBaseBack;
+    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::audioConfigBaseFromHal(halConfigBase, false /*isInput*/, &configBaseBack));
-    EXPECT_EQ(configBase, configBaseBack);
+              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));
 }
 
@@ -202,6 +404,7 @@
     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));
 }
 
@@ -233,9 +436,16 @@
 // The enums module is too small to have unit tests on its own.
 TEST(HidlUtils, VendorExtension) {
     EXPECT_TRUE(xsd::isVendorExtension("VX_GOOGLE_VR_42"));
+    EXPECT_TRUE(xsd::isVendorExtension("VX_QCM_SPK"));
+    EXPECT_FALSE(xsd::isVendorExtension(""));
     EXPECT_FALSE(xsd::isVendorExtension("random string"));
     EXPECT_FALSE(xsd::isVendorExtension("VX_"));
+    EXPECT_FALSE(xsd::isVendorExtension("VX_X"));
+    EXPECT_FALSE(xsd::isVendorExtension("VX_X_"));
+    EXPECT_FALSE(xsd::isVendorExtension("VX_X_X"));
+    EXPECT_FALSE(xsd::isVendorExtension("VX_XX_X"));
     EXPECT_FALSE(xsd::isVendorExtension("VX_GOOGLE_$$"));
+    EXPECT_FALSE(xsd::isVendorExtension("VX_$CM_SPK"));
 }
 
 TEST(HidlUtils, ConvertInvalidDeviceAddress) {
@@ -276,6 +486,11 @@
     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) {
@@ -347,6 +562,9 @@
     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));
 }
 
@@ -357,8 +575,9 @@
         AudioFormat formatBack;
         EXPECT_EQ(NO_ERROR, HidlUtils::audioFormatToHal(format, &halFormat))
                 << "Conversion of \"" << format << "\" failed";
-        EXPECT_TRUE(audio_is_valid_format(halFormat))
-                << "Converted format \"" << format << "\" is invalid";
+        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);
@@ -430,6 +649,9 @@
     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));
 }
 
@@ -456,6 +678,15 @@
     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);
@@ -524,6 +755,7 @@
     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));
 }
 
@@ -543,7 +775,7 @@
 TEST(HidlUtils, ConvertInvalidOffloadInfo) {
     AudioOffloadInfo invalid;
     audio_offload_info_t halInvalid = AUDIO_INFO_INITIALIZER;
-    halInvalid.channel_mask = AUDIO_CHANNEL_INVALID;
+    halInvalid.channel_mask = kInvalidHalChannelMask;
     halInvalid.format = kInvalidHalFormat;
     EXPECT_EQ(BAD_VALUE, HidlUtils::audioOffloadInfoFromHal(halInvalid, &invalid));
     invalid.base.channelMask = "random string";
@@ -553,9 +785,7 @@
 
 TEST(HidlUtils, ConvertOffloadInfo) {
     AudioOffloadInfo offloadInfo = {};
-    offloadInfo.base.sampleRateHz = 44100;
-    offloadInfo.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
-    offloadInfo.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    offloadInfo.base = generateValidConfigBase(false /*isInput*/);
     offloadInfo.streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC);
     offloadInfo.bitRatePerSecond = 320;
     offloadInfo.durationMicroseconds = -1;
@@ -574,33 +804,76 @@
 
 TEST(HidlUtils, ConvertInvalidConfig) {
     AudioConfig invalid;
-    audio_config_t halInvalid = AUDIO_CONFIG_INITIALIZER;
-    halInvalid.channel_mask = AUDIO_CHANNEL_INVALID;
-    halInvalid.format = kInvalidHalFormat;
-    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigFromHal(halInvalid, false /*isInput*/, &invalid));
-    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigFromHal(halInvalid, true /*isInput*/, &invalid));
-    invalid.base.channelMask = "random string";
-    invalid.base.format = "random string";
-    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigToHal(invalid, &halInvalid));
+    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 config = {};
-    config.base.sampleRateHz = 44100;
-    config.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
-    config.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
-    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);
+    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.sampleRateHz = 44100;
-    config.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
-    config.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    config.base = generateValidConfigBase(false /*isInput*/);
     config.offloadInfo.info(
             AudioOffloadInfo{.base = config.base,
                              .streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC),
@@ -655,18 +928,18 @@
     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 = AUDIO_CHANNEL_INVALID;
+    halInvalid.channel_mask = kInvalidHalChannelMask;
     EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortConfigFromHal(halInvalid, &invalid));
-    invalid.base.channelMask = "random string";
+    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 = 44100;
-    config.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
-    config.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    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({});
@@ -681,6 +954,53 @@
     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 = {};
@@ -689,8 +1009,10 @@
     halInvalid.num_audio_profiles = 1;
     halInvalid.audio_profiles[0].format = kInvalidHalFormat;
     EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortFromHal(halInvalid, &invalid));
-    invalid.profiles.resize(1);
-    invalid.profiles[0].format = "random string";
+    invalid.transports.resize(1);
+    AudioProfile invalidProfile;
+    invalidProfile.format = "random string";
+    invalid.transports[0].audioCapability.profile(invalidProfile);
     EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortToHal(invalid, &halInvalid));
 }
 
@@ -698,14 +1020,22 @@
     AudioPort port = {};
     port.id = 42;
     port.name = "test";
-    port.profiles.resize(1);
-    port.profiles[0].format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
-    port.profiles[0].sampleRates.resize(2);
-    port.profiles[0].sampleRates[0] = 44100;
-    port.profiles[0].sampleRates[1] = 48000;
-    port.profiles[0].channelMasks.resize(2);
-    port.profiles[0].channelMasks[0] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO);
-    port.profiles[0].channelMasks[1] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    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({});
@@ -734,7 +1064,7 @@
             {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_$$"}};
+    hidl_vec<AudioTag> notExtensions = {{"", "random string", "VX_", "VX_GOOGLE_$$"}};
     EXPECT_EQ(BAD_VALUE, HidlUtils::audioTagsToHal(notExtensions, halTag));
 }
 
@@ -743,20 +1073,51 @@
     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(halEmptyTags, &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(halOneTag, &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(halTwoTags, &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..1602d25 100644
--- a/audio/common/all-versions/test/utility/Android.bp
+++ b/audio/common/all-versions/test/utility/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.audio.common.test.utility",
     defaults : ["hidl_defaults"],
@@ -29,4 +38,3 @@
     static_libs: ["libgtest"],
     export_static_lib_headers: ["libgtest"],
 }
-
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/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 e0f0860..f61964e 100644
--- a/audio/core/all-versions/default/Android.bp
+++ b/audio/core/all-versions/default/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"],
+}
+
 filegroup {
     name: "android.hardware.audio-impl_srcs",
     srcs: [
-        "Conversions.cpp",
         "Device.cpp",
         "DevicesFactory.cpp",
         "ParametersUtil.cpp",
@@ -63,6 +71,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",
     ],
@@ -79,6 +88,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",
     ],
@@ -94,6 +104,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",
     ],
@@ -109,6 +120,7 @@
     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",
     ],
@@ -129,11 +141,11 @@
     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",
-        "libxml2",
     ],
     cflags: [
         "-DMAJOR_VERSION=7",
diff --git a/audio/core/all-versions/default/Conversions.cpp b/audio/core/all-versions/default/Conversions.cpp
deleted file mode 100644
index f1752cc..0000000
--- a/audio/core/all-versions/default/Conversions.cpp
+++ /dev/null
@@ -1,230 +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>
-
-#if MAJOR_VERSION >= 7
-#include <android_audio_policy_configuration_V7_0-enums.h>
-#endif
-#include <HidlUtils.h>
-#include <log/log.h>
-
-namespace android {
-namespace hardware {
-namespace audio {
-namespace CPP_VERSION {
-namespace implementation {
-
-using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
-
-#define CONVERT_CHECKED(expr, result)                   \
-    if (status_t status = (expr); status != NO_ERROR) { \
-        result = status;                                \
-    }
-
-status_t 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 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
-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] = AudioMicrophoneChannelMapping(src.channel_mapping[ch]);
-        }
-        pDst->location = AudioMicrophoneLocation(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 = AudioMicrophoneDirectionality(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;
-}
-
-status_t sinkMetadataToHal(const SinkMetadata& sinkMetadata,
-                           std::vector<record_track_metadata>* halTracks) {
-    status_t result = NO_ERROR;
-    if (halTracks != nullptr) {
-        halTracks->reserve(sinkMetadata.tracks.size());
-    }
-    for (auto& metadata : sinkMetadata.tracks) {
-        record_track_metadata halTrackMetadata{.gain = metadata.gain};
-        CONVERT_CHECKED(HidlUtils::audioSourceToHal(metadata.source, &halTrackMetadata.source),
-                        result);
-#if MAJOR_VERSION >= 5
-        if (metadata.destination.getDiscriminator() ==
-            RecordTrackMetadata::Destination::hidl_discriminator::device) {
-            CONVERT_CHECKED(
-                    deviceAddressToHal(metadata.destination.device(), &halTrackMetadata.dest_device,
-                                       halTrackMetadata.dest_device_address),
-                    result);
-        }
-#endif
-        if (halTracks != nullptr) {
-            halTracks->push_back(std::move(halTrackMetadata));
-        }
-    }
-    return result;
-}
-
-status_t 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& metadata : sourceMetadata.tracks) {
-        playback_track_metadata_t halTrackMetadata{.gain = metadata.gain};
-        CONVERT_CHECKED(HidlUtils::audioUsageToHal(metadata.usage, &halTrackMetadata.usage),
-                        result);
-        CONVERT_CHECKED(HidlUtils::audioContentTypeToHal(metadata.contentType,
-                                                         &halTrackMetadata.content_type),
-                        result);
-        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;
-}
-
-bool audioInputFlagsToHal(const hidl_vec<AudioInOutFlag>& flags, audio_input_flags_t* halFlags) {
-    bool success = true;
-    *halFlags = {};
-    for (const auto& flag : flags) {
-        audio_input_flags_t halFlag;
-        if (!xsd::isUnknownAudioInOutFlag(flag) &&
-            audio_input_flag_from_string(flag.c_str(), &halFlag)) {
-            *halFlags = static_cast<audio_input_flags_t>(*halFlags | halFlag);
-        } else {
-            ALOGE("Unknown audio input flag \"%s\"", flag.c_str());
-            success = false;
-        }
-    }
-    return success;
-}
-
-bool audioOutputFlagsToHal(const hidl_vec<AudioInOutFlag>& flags, audio_output_flags_t* halFlags) {
-    bool success = true;
-    *halFlags = {};
-    for (const auto& flag : flags) {
-        audio_output_flags_t halFlag;
-        if (!xsd::isUnknownAudioInOutFlag(flag) &&
-            audio_output_flag_from_string(flag.c_str(), &halFlag)) {
-            *halFlags = static_cast<audio_output_flags_t>(*halFlags | halFlag);
-        } else {
-            ALOGE("Unknown audio output flag \"%s\"", flag.c_str());
-            success = false;
-        }
-    }
-    return success;
-}
-
-status_t sinkMetadataToHalV7(const SinkMetadata& sinkMetadata,
-                             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(bases.size());
-    }
-    auto baseIter = std::make_move_iterator(bases.begin());
-    for (auto& metadata : sinkMetadata.tracks) {
-        record_track_metadata_v7_t halTrackMetadata;
-        CONVERT_CHECKED(HidlUtils::audioChannelMaskToHal(metadata.channelMask,
-                                                         &halTrackMetadata.channel_mask),
-                        result);
-        CONVERT_CHECKED(HidlUtils::audioTagsToHal(metadata.tags, halTrackMetadata.tags), result);
-        if (halTracks != nullptr) {
-            halTrackMetadata.base = std::move(*baseIter++);
-            halTracks->push_back(std::move(halTrackMetadata));
-        }
-    }
-    return result;
-}
-
-status_t sourceMetadataToHalV7(const SourceMetadata& sourceMetadata,
-                               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(bases.size());
-    }
-    auto baseIter = std::make_move_iterator(bases.begin());
-    for (auto& metadata : sourceMetadata.tracks) {
-        playback_track_metadata_v7_t halTrackMetadata;
-        CONVERT_CHECKED(HidlUtils::audioChannelMaskToHal(metadata.channelMask,
-                                                         &halTrackMetadata.channel_mask),
-                        result);
-        CONVERT_CHECKED(HidlUtils::audioTagsToHal(metadata.tags, halTrackMetadata.tags), result);
-        if (halTracks != nullptr) {
-            halTrackMetadata.base = std::move(*baseIter++);
-            halTracks->push_back(std::move(halTrackMetadata));
-        }
-    }
-    return result;
-}
-#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 7caed44..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 {
@@ -160,11 +160,11 @@
     audio_stream_out_t* halStream;
     audio_devices_t halDevice;
     char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
-    if (deviceAddressToHal(device, &halDevice, halDeviceAddress) != NO_ERROR) {
+    if (CoreUtils::deviceAddressToHal(device, &halDevice, halDeviceAddress) != NO_ERROR) {
         return {Result::INVALID_ARGUMENTS, nullptr};
     }
     audio_output_flags_t halFlags;
-    if (!audioOutputFlagsToHal(flags, &halFlags)) {
+    if (CoreUtils::audioOutputFlagsToHal(flags, &halFlags) != NO_ERROR) {
         return {Result::INVALID_ARGUMENTS, nullptr};
     }
     ALOGV("open_output_stream handle: %d devices: %x flags: %#x "
@@ -195,12 +195,12 @@
     audio_stream_in_t* halStream;
     audio_devices_t halDevice;
     char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
-    if (deviceAddressToHal(device, &halDevice, halDeviceAddress) != NO_ERROR) {
+    if (CoreUtils::deviceAddressToHal(device, &halDevice, halDeviceAddress) != NO_ERROR) {
         return {Result::INVALID_ARGUMENTS, nullptr};
     }
     audio_input_flags_t halFlags;
     audio_source_t halSource;
-    if (!audioInputFlagsToHal(flags, &halFlags) ||
+    if (CoreUtils::audioInputFlagsToHal(flags, &halFlags) != NO_ERROR ||
         HidlUtils::audioSourceToHal(source, &halSource) != NO_ERROR) {
         return {Result::INVALID_ARGUMENTS, nullptr};
     }
@@ -254,9 +254,12 @@
                                       const SourceMetadata& sourceMetadata,
                                       openOutputStream_cb _hidl_cb) {
 #if MAJOR_VERSION <= 6
-    if (status_t status = sourceMetadataToHal(sourceMetadata, nullptr); status != NO_ERROR) {
+    if (status_t status = CoreUtils::sourceMetadataToHal(sourceMetadata, nullptr);
+        status != NO_ERROR) {
 #else
-    if (status_t status = sourceMetadataToHalV7(sourceMetadata, nullptr); status != NO_ERROR) {
+    if (status_t status = CoreUtils::sourceMetadataToHalV7(sourceMetadata,
+                                                           false /*ignoreNonVendorTags*/, nullptr);
+        status != NO_ERROR) {
 #endif
         _hidl_cb(analyzeStatus("sourceMetadataToHal", status), nullptr, AudioConfig{});
         return Void();
@@ -288,9 +291,11 @@
         return Void();
     }
 #if MAJOR_VERSION <= 6
-    if (status_t status = sinkMetadataToHal(sinkMetadata, nullptr); status != NO_ERROR) {
+    if (status_t status = CoreUtils::sinkMetadataToHal(sinkMetadata, nullptr); status != NO_ERROR) {
 #else
-    if (status_t status = sinkMetadataToHalV7(sinkMetadata, nullptr); status != NO_ERROR) {
+    if (status_t status = CoreUtils::sinkMetadataToHalV7(sinkMetadata,
+                                                         false /*ignoreNonVendorTags*/, nullptr);
+        status != NO_ERROR) {
 #endif
         _hidl_cb(analyzeStatus("sinkMetadataToHal", status), nullptr, AudioConfig{});
         return Void();
@@ -355,18 +360,43 @@
     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;
@@ -444,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;
     }
diff --git a/audio/core/all-versions/default/ParametersUtil.cpp b/audio/core/all-versions/default/ParametersUtil.cpp
index 694eb73..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 {
@@ -153,7 +154,7 @@
 Result ParametersUtil::setParam(const char* name, const DeviceAddress& address) {
     audio_devices_t halDeviceType;
     char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
-    if (deviceAddressToHal(address, &halDeviceType, halDeviceAddress) != NO_ERROR) {
+    if (CoreUtils::deviceAddressToHal(address, &halDeviceType, halDeviceAddress) != NO_ERROR) {
         return Result::INVALID_ARGUMENTS;
     }
     AudioParameter params{String8(halDeviceAddress)};
diff --git a/audio/core/all-versions/default/Stream.cpp b/audio/core/all-versions/default/Stream.cpp
index f964cbb..7e32573 100644
--- a/audio/core/all-versions/default/Stream.cpp
+++ b/audio/core/all-versions/default/Stream.cpp
@@ -19,7 +19,6 @@
 #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>
@@ -30,6 +29,7 @@
 #include <hardware/audio_effect.h>
 #include <media/AudioContainers.h>
 #include <media/TypeConverter.h>
+#include <util/CoreUtils.h>
 
 namespace android {
 namespace hardware {
@@ -278,23 +278,36 @@
     return Void();
 }
 
-Return<Result> Stream::setAudioProperties(const AudioConfigBase& config) {
-    audio_config_base_t halConfigBase = {};
-    status_t status = HidlUtils::audioConfigBaseToHal(config, &halConfigBase);
+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 (Result result = setParam(AudioParameter::keySamplingRate,
-                                 static_cast<int>(halConfigBase.sample_rate));
-        result != Result::OK) {
-        return result;
+    if (sRateSpecified) {
+        if (Result result = setParam(AudioParameter::keySamplingRate,
+                                     static_cast<int>(halConfigBase.sample_rate));
+            result != Result::OK) {
+            return result;
+        }
     }
-    if (Result result =
-                setParam(AudioParameter::keyChannels, static_cast<int>(halConfigBase.channel_mask));
-        result != Result::OK) {
-        return result;
+    if (channelMaskSpecified) {
+        if (Result result = setParam(AudioParameter::keyChannels,
+                                     static_cast<int>(halConfigBase.channel_mask));
+            result != Result::OK) {
+            return result;
+        }
     }
-    return setParam(AudioParameter::keyFormat, static_cast<int>(halConfigBase.format));
+    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
@@ -360,9 +373,10 @@
     hidl_vec<DeviceAddress> devices;
     if (retval == Result::OK) {
         devices.resize(1);
-        retval = Stream::analyzeStatus("get_devices",
-                                       deviceAddressFromHal(static_cast<audio_devices_t>(halDevice),
-                                                            nullptr, &devices[0]));
+        retval = Stream::analyzeStatus(
+                "get_devices",
+                CoreUtils::deviceAddressFromHal(static_cast<audio_devices_t>(halDevice), nullptr,
+                                                &devices[0]));
     }
     _hidl_cb(retval, devices);
     return Void();
diff --git a/audio/core/all-versions/default/StreamIn.cpp b/audio/core/all-versions/default/StreamIn.cpp
index 2c5e9f1..599f3c3 100644
--- a/audio/core/all-versions/default/StreamIn.cpp
+++ b/audio/core/all-versions/default/StreamIn.cpp
@@ -17,7 +17,6 @@
 #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"
 
@@ -27,6 +26,7 @@
 #include <HidlUtils.h>
 #include <android/log.h>
 #include <hardware/audio.h>
+#include <util/CoreUtils.h>
 #include <utils/Trace.h>
 #include <cmath>
 #include <memory>
@@ -233,7 +233,7 @@
     return mStreamCommon->getSupportedProfiles(_hidl_cb);
 }
 
-Return<Result> StreamIn::setAudioProperties(const AudioConfigBase& config) {
+Return<Result> StreamIn::setAudioProperties(const AudioConfigBaseOptional& config) {
     return mStreamCommon->setAudioProperties(config);
 }
 
@@ -481,13 +481,15 @@
 Result StreamIn::doUpdateSinkMetadata(const SinkMetadata& sinkMetadata) {
     std::vector<record_track_metadata> halTracks;
 #if MAJOR_VERSION <= 6
-    (void)sinkMetadataToHal(sinkMetadata, &halTracks);
+    (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 = sinkMetadataToHalV7(sinkMetadata, &halTracksV7); status == NO_ERROR) {
+    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));
@@ -507,7 +509,9 @@
 #if MAJOR_VERSION >= 7
 Result StreamIn::doUpdateSinkMetadataV7(const SinkMetadata& sinkMetadata) {
     std::vector<record_track_metadata_v7> halTracks;
-    if (status_t status = sinkMetadataToHalV7(sinkMetadata, &halTracks); status != NO_ERROR) {
+    if (status_t status = CoreUtils::sinkMetadataToHalV7(sinkMetadata,
+                                                         false /*ignoreNonVendorTags*/, &halTracks);
+        status != NO_ERROR) {
         return Stream::analyzeStatus("sinkMetadataToHal", status);
     }
     const sink_metadata_v7_t halMetadata = {
@@ -553,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 ffd3b6b..a089f6b 100644
--- a/audio/core/all-versions/default/StreamOut.cpp
+++ b/audio/core/all-versions/default/StreamOut.cpp
@@ -17,7 +17,6 @@
 #define LOG_TAG "StreamOutHAL"
 
 #include "core/default/StreamOut.h"
-#include "core/default/Conversions.h"
 #include "core/default/Util.h"
 
 //#define LOG_NDEBUG 0
@@ -30,6 +29,7 @@
 #include <HidlUtils.h>
 #include <android/log.h>
 #include <hardware/audio.h>
+#include <util/CoreUtils.h>
 #include <utils/Trace.h>
 
 namespace android {
@@ -239,7 +239,7 @@
     return mStreamCommon->getSupportedProfiles(_hidl_cb);
 }
 
-Return<Result> StreamOut::setAudioProperties(const AudioConfigBase& config) {
+Return<Result> StreamOut::setAudioProperties(const AudioConfigBaseOptional& config) {
     return mStreamCommon->setAudioProperties(config);
 }
 
@@ -589,13 +589,15 @@
 Result StreamOut::doUpdateSourceMetadata(const SourceMetadata& sourceMetadata) {
     std::vector<playback_track_metadata_t> halTracks;
 #if MAJOR_VERSION <= 6
-    (void)sourceMetadataToHal(sourceMetadata, &halTracks);
+    (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 = sourceMetadataToHalV7(sourceMetadata, &halTracksV7); status == NO_ERROR) {
+    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));
@@ -615,7 +617,9 @@
 #if MAJOR_VERSION >= 7
 Result StreamOut::doUpdateSourceMetadataV7(const SourceMetadata& sourceMetadata) {
     std::vector<playback_track_metadata_v7> halTracks;
-    if (status_t status = sourceMetadataToHalV7(sourceMetadata, &halTracks); status != NO_ERROR) {
+    if (status_t status = CoreUtils::sourceMetadataToHalV7(
+                sourceMetadata, false /*ignoreNonVendorTags*/, &halTracks);
+        status != NO_ERROR) {
         return Stream::analyzeStatus("sourceMetadataToHal", status);
     }
     const source_metadata_v7_t halMetadata = {
@@ -658,32 +662,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) {
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 61720d5..0000000
--- a/audio/core/all-versions/default/include/core/default/Conversions.h
+++ /dev/null
@@ -1,83 +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>
-
-#include <VersionUtils.h>
-
-namespace android {
-namespace hardware {
-namespace audio {
-namespace CPP_VERSION {
-namespace implementation {
-
-using ::android::hardware::hidl_vec;
-using namespace ::android::hardware::audio::common::CPP_VERSION;
-using namespace ::android::hardware::audio::CPP_VERSION;
-
-status_t deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
-                            char* halDeviceAddress);
-status_t deviceAddressFromHal(audio_devices_t halDeviceType, const char* halDeviceAddress,
-                              DeviceAddress* device);
-
-#if MAJOR_VERSION >= 4
-bool halToMicrophoneCharacteristics(MicrophoneInfo* pDst,
-                                    const struct audio_microphone_characteristic_t& src);
-status_t sinkMetadataToHal(const SinkMetadata& sinkMetadata,
-                           std::vector<record_track_metadata>* halTracks);
-status_t sourceMetadataToHal(const SourceMetadata& sourceMetadata,
-                             std::vector<playback_track_metadata_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;
-
-inline bool audioInputFlagsToHal(AudioInputFlags flags, audio_input_flags_t* halFlags) {
-    *halFlags = static_cast<audio_input_flags_t>(flags);
-    return true;
-}
-
-inline bool audioOutputFlagsToHal(AudioOutputFlags flags, audio_output_flags_t* halFlags) {
-    *halFlags = static_cast<audio_output_flags_t>(flags);
-    return true;
-}
-#else
-bool audioInputFlagsToHal(const hidl_vec<AudioInOutFlag>& flags, audio_input_flags_t* halFlags);
-bool audioOutputFlagsToHal(const hidl_vec<AudioInOutFlag>& flags, audio_output_flags_t* halFlags);
-// Overloading isn't convenient when passing a nullptr.
-status_t sinkMetadataToHalV7(const SinkMetadata& sinkMetadata,
-                             std::vector<record_track_metadata_v7_t>* halTracks);
-status_t sourceMetadataToHalV7(const SourceMetadata& sourceMetadata,
-                               std::vector<playback_track_metadata_v7_t>* halTracks);
-#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 2a4d226..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,17 +44,10 @@
 using ::android::hardware::hidl_vec;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
-#if MAJOR_VERSION <= 6
-using AudioInputFlags =
-        ::android::hardware::audio::common::CPP_VERSION::implementation::AudioInputFlagBitfield;
-using AudioOutputFlags =
-        ::android::hardware::audio::common::CPP_VERSION::implementation::AudioOutputFlagBitfield;
-#else
-using AudioInputFlags = hidl_vec<::android::hardware::audio::CPP_VERSION::AudioInOutFlag>;
-using AudioOutputFlags = hidl_vec<::android::hardware::audio::CPP_VERSION::AudioInOutFlag>;
-#endif
 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);
@@ -159,6 +153,10 @@
     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;
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 0865992..66d60e3 100644
--- a/audio/core/all-versions/default/include/core/default/Stream.h
+++ b/audio/core/all-versions/default/include/core/default/Stream.h
@@ -77,7 +77,7 @@
     Return<Result> setFormat(AudioFormat format) override;
 #else
     Return<void> getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) override;
-    Return<Result> setAudioProperties(const AudioConfigBase& config) 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;
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 651b3a6..a980f3f 100644
--- a/audio/core/all-versions/default/include/core/default/StreamIn.h
+++ b/audio/core/all-versions/default/include/core/default/StreamIn.h
@@ -72,7 +72,7 @@
     Return<Result> setFormat(AudioFormat format) override;
 #else
     Return<void> getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) override;
-    Return<Result> setAudioProperties(const AudioConfigBase& config) 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;
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 b8e8515..ccc1c1a 100644
--- a/audio/core/all-versions/default/include/core/default/StreamOut.h
+++ b/audio/core/all-versions/default/include/core/default/StreamOut.h
@@ -72,7 +72,7 @@
     Return<Result> setFormat(AudioFormat format) override;
 #else
     Return<void> getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) override;
-    Return<Result> setAudioProperties(const AudioConfigBase& config) 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;
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 5076dcf..b96cc83 100644
--- a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
@@ -77,7 +77,6 @@
                   .tags = {},
                   .channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO)}}};
 #endif
-        EventFlag* efGroup;
         for (auto microphone : microphones) {
 #if MAJOR_VERSION <= 6
             if (microphone.deviceAddress.device != AudioDevice::IN_BUILTIN_MIC) {
@@ -96,44 +95,15 @@
                                                             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());
         }
     }
 }
@@ -322,9 +292,9 @@
                 const SourceMetadata metadata = {
                         {{toString(usage),
                           toString(content),
-                          {} /* tags */,
+                          volume,
                           toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
-                          volume}}};
+                          {} /* tags */}}};
                 ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata(metadata))
                         << "usage=" << toString(usage) << ", content=" << toString(content)
                         << ", volume=" << volume;
@@ -336,24 +306,24 @@
     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),
-                      0.1},
+                      {}}, // tags
           {toString(xsd::AudioUsage::AUDIO_USAGE_VOICE_COMMUNICATION),
                       toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_SPEECH),
-                      {}, // tags
+                      1.0,
                       toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO),
-                      1.0},
+                      {}},
           {toString(xsd::AudioUsage::AUDIO_USAGE_ALARM),
                       toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_SONIFICATION),
-                      {}, // tags
+                      0.0,
                       toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
-                      0.0},
+                      {}},
           {toString(xsd::AudioUsage::AUDIO_USAGE_ASSISTANT),
                       toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_UNKNOWN),
-                      {},
+                      0.3,
                       toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO),
-                      0.3}}}
+                      {}}}}
     ));
     // clang-format on
     // Set no metadata as if all stream track had stopped
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 0ebe4c2..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,105 +17,6 @@
 // pull in all the <= 5.0 tests
 #include "5.0/AudioPrimaryHidlHalTest.cpp"
 
-#if MAJOR_VERSION <= 6
-static 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;
-}
-
-static 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;
-}
-#endif  // MAJOR_VERSION <= 6
-
 class SingleConfigOutputStreamTest : public OutputStreamTest {};
 TEST_P(SingleConfigOutputStreamTest, CloseDeviceWithOpenedOutputStreams) {
     doc::test("Verify that a device can't be closed if there are output streams opened");
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
index 7fca610..0b3098b 100644
--- a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
@@ -14,277 +14,13 @@
  * 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"
 
-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
-    };
-}
-
-static 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, 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 = true) {
-    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, config, validFlags);
-                    }
-                    {
-                        AudioConfig config{.base = validBase};
-                        config.base.format = "random_string";
-                        if (isOffload) {
-                            config.offloadInfo.info(generateOffloadInfo(validBase));
-                        }
-                        result.emplace_back(device, 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, config, flags);
-                    }
-                    if (isOffload) {
-                        {
-                            AudioConfig config{.base = validBase};
-                            config.offloadInfo.info(generateOffloadInfo(validBase));
-                            config.offloadInfo.info().base.channelMask = "random_string";
-                        }
-                        {
-                            AudioConfig config{.base = validBase};
-                            config.offloadInfo.info(generateOffloadInfo(validBase));
-                            config.offloadInfo.info().base.format = "random_string";
-                        }
-                        {
-                            AudioConfig config{.base = validBase};
-                            config.offloadInfo.info(generateOffloadInfo(validBase));
-                            config.offloadInfo.info().streamType = "random_string";
-                        }
-                        {
-                            AudioConfig config{.base = validBase};
-                            config.offloadInfo.info(generateOffloadInfo(validBase));
-                            config.offloadInfo.info().usage = "random_string";
-                        }
-                        hasOffloadConfig = true;
-                    } else {
-                        hasRegularConfig = true;
-                    }
-                    break;
-                }
-                if (hasOffloadConfig && hasRegularConfig) break;
-            }
-        }
-        return result;
-    }();
-    return parameters;
-}
-
-static 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, 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 = true) {
-    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, config, validFlags);
-                    }
-                    {
-                        AudioConfig config{.base = validBase};
-                        config.base.format = "random_string";
-                        result.emplace_back(device, config, validFlags);
-                    }
-                    if (generateInvalidFlags) {
-                        AudioConfig config{.base = validBase};
-                        std::vector<AudioInOutFlag> flags = {"random_string", ""};
-                        result.emplace_back(device, config, flags);
-                    }
-                    hasConfig = true;
-                    break;
-                }
-                if (hasConfig) break;
-            }
-        }
-        return result;
-    }();
-    return parameters;
-}
-
 class InvalidInputConfigNoFlagsTest : public AudioHidlTestWithDeviceConfigParameter {};
 TEST_P(InvalidInputConfigNoFlagsTest, InputBufferSizeTest) {
     doc::test("Verify that invalid config is rejected by IDevice::getInputBufferSize method.");
@@ -367,10 +103,10 @@
     static std::vector<AudioPortConfig> invalids = [&] {
         std::vector<AudioPortConfig> result;
         AudioPortConfig invalidBaseChannelMask = valids[PORT_CONF_MINIMAL];
-        invalidBaseChannelMask.base.channelMask = "random_string";
+        invalidBaseChannelMask.base.channelMask.value("random_string");
         result.push_back(std::move(invalidBaseChannelMask));
         AudioPortConfig invalidBaseFormat = valids[PORT_CONF_MINIMAL];
-        invalidBaseFormat.base.format = "random_string";
+        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"}};
@@ -724,19 +460,19 @@
         areAudioPatchesSupported() ? doc::partialTest("Audio patches are supported")
                                    : testSetDevicesInvalidDeviceAddress(stream.get()));
 
-static void testSetAudioPropertiesInvalidArguments(IStream* stream, const AudioConfigBase& base) {
-    AudioConfigBase invalidFormat = base;
-    invalidFormat.format = "random_string";
+static void testSetAudioPropertiesInvalidArguments(IStream* stream) {
+    AudioConfigBaseOptional invalidFormat;
+    invalidFormat.format.value("random_string");
     ASSERT_RESULT(invalidArgsOrNotSupported, stream->setAudioProperties(invalidFormat));
 
-    AudioConfigBase invalidChannelMask = base;
-    invalidChannelMask.channelMask = "random_string";
+    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(), audioConfig.base));
+        testSetAudioPropertiesInvalidArguments(stream.get()));
 
 TEST_P(SingleConfigOutputStreamTest, UpdateInvalidSourceMetadata) {
     doc::test("Verify that invalid metadata is rejected by IStreamOut::updateSourceMetadata");
@@ -753,3 +489,353 @@
                 << ::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
index 7d88642..f798839 100644
--- a/audio/core/all-versions/vts/functional/7.0/PolicyConfig.h
+++ b/audio/core/all-versions/vts/functional/7.0/PolicyConfig.h
@@ -16,11 +16,26 @@
 
 #pragma once
 
-// Note: it is assumed that this file is included from AudioPrimaryHidlTest.h
-// and thus it doesn't have all '#include' and 'using' directives required
-// for a standalone compilation.
+#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;
 }
 
@@ -30,68 +45,57 @@
         : mConfigFileName{configFileName},
           mFilePath{findExistingConfigurationFile(mConfigFileName)},
           mConfig{xsd::read(mFilePath.c_str())} {
-        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());
-                        }
-                    }
-                }
-            }
-        }
+        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;
-        }
+    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 {
-        if (mConfig && mConfig->getFirstModules()) {
-            for (const auto& module : mConfig->getFirstModules()->get_module()) {
-                if (module.getName() == name) return &module;
-            }
-        }
-        return nullptr;
-    }
+    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;
     }
-    bool 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;
+    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) {
-        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{};
-    }
+    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;
-    status_t mStatus = NO_INIT;
+    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 c7bfe08..9183191 100644
--- a/audio/core/all-versions/vts/functional/Android.bp
+++ b/audio/core/all-versions/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: "VtsHalAudioTargetTest_defaults",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -117,6 +126,7 @@
     defaults: ["VtsHalAudioTargetTest_defaults"],
     srcs: [
         "6.0/AudioPrimaryHidlHalTest.cpp",
+        "6.0/Generators.cpp",
     ],
     static_libs: [
         "libaudiofoundation",
@@ -143,11 +153,16 @@
     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",
@@ -161,3 +176,67 @@
     // 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 f145b60..aa7fd8e 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -59,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"
@@ -88,6 +89,10 @@
 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 {
@@ -99,6 +104,7 @@
 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};
@@ -107,9 +113,14 @@
 #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:
@@ -175,9 +186,6 @@
 //////////////////// 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);
@@ -509,24 +517,6 @@
 // list is empty, this isn't a problem.
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioPatchHidlTest);
 
-// 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 };
-#if MAJOR_VERSION <= 6
-enum { INDEX_INPUT, INDEX_OUTPUT };
-using DeviceConfigParameter =
-        std::tuple<DeviceParameter, AudioConfig, std::variant<AudioInputFlag, AudioOutputFlag>>;
-#elif MAJOR_VERSION >= 7
-using DeviceConfigParameter = std::tuple<DeviceParameter, AudioConfig, std::vector<AudioInOutFlag>>;
-#endif
-
-#if MAJOR_VERSION >= 6
-const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters();
-const std::vector<DeviceConfigParameter>& getInputDeviceSingleConfigParameters();
-const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters();
-const std::vector<DeviceConfigParameter>& getOutputDeviceSingleConfigParameters();
-#endif
-
 #if MAJOR_VERSION >= 4
 static std::string SanitizeStringForGTestName(const std::string& s) {
     std::string result = s;
@@ -794,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 {
@@ -803,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{};
         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);
@@ -822,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);
@@ -850,8 +842,10 @@
 #endif
         }
     }
+    AudioIoHandle getIoHandle() const { return mIoHandle; }
 
   private:
+    const AudioIoHandle mIoHandle = getNextIoHandle();
     sp<Stream>& mStream;
 };
 
@@ -877,7 +871,6 @@
         return res;
     }
 
-  private:
     void TearDown() override {
         if (open) {
             ASSERT_OK(closeStream());
@@ -895,6 +888,116 @@
 
 ////////////////////////////// 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
@@ -928,9 +1031,9 @@
     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),
-                1 /* gain */ } }};
+                {} } }};
 #endif
 };
 TEST_P(OutputStreamTest, OpenOutputStreamTest) {
@@ -970,13 +1073,138 @@
 
 ////////////////////////////// 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
-        address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT);
+        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();
@@ -994,7 +1222,8 @@
 #elif MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6
      const SinkMetadata initMetadata = {{ {.source = AudioSource::DEFAULT, .gain = 1 } }};
 #elif MAJOR_VERSION >= 7
-     const SinkMetadata initMetadata = {
+     const std::string& getMixPortName() const { return std::get<PARAM_PORT_NAME>(GetParam()); }
+     SinkMetadata initMetadata = {
              {{.source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
                .gain = 1,
                .tags = {},
@@ -1155,13 +1384,14 @@
     for (const auto& profile : profiles) {
         for (const auto& sampleRate : profile.sampleRates) {
             for (const auto& channelMask : profile.channelMasks) {
-                AudioConfigBase config{.format = profile.format,
-                                       .sampleRateHz = sampleRate,
-                                       .channelMask = {{channelMask}}};
+                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) << config.format << "; " << config.sampleRateHz << "; "
-                                           << toString(config.channelMask);
+                EXPECT_EQ(Result::OK, ret)
+                        << profile.format << "; " << sampleRate << "; " << channelMask;
             }
         }
     }
@@ -1169,7 +1399,7 @@
 
 TEST_IO_STREAM(SetAudioProperties, "Call setAudioProperties for all supported profiles",
                testSetAudioProperties(stream.get()))
-#endif
+#endif  // MAJOR_VERSION <= 6
 
 static void testGetAudioProperties(IStream* stream, AudioConfig expectedConfig) {
 #if MAJOR_VERSION <= 6
@@ -1392,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);
@@ -1575,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;
@@ -1595,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);
 }
 
 //////////////////////////////////////////////////////////////////////////////
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 1a1dbea..a2bb1ee 100644
--- a/audio/core/all-versions/vts/functional/ConfigHelper.h
+++ b/audio/core/all-versions/vts/functional/ConfigHelper.h
@@ -16,10 +16,21 @@
 
 #pragma once
 
-// Code in this file uses 'getCachedPolicyConfig'
-#ifndef AUDIO_PRIMARY_HIDL_HAL_TEST
-#error Must be included from AudioPrimaryHidlTest.h
-#endif
+#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 ///////////////
@@ -35,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) {
diff --git a/audio/core/all-versions/vts/functional/DeviceManager.h b/audio/core/all-versions/vts/functional/DeviceManager.h
index 6efed79..6db78a7 100644
--- a/audio/core/all-versions/vts/functional/DeviceManager.h
+++ b/audio/core/all-versions/vts/functional/DeviceManager.h
@@ -16,9 +16,27 @@
 
 #pragma once
 
-// Note: it is assumed that this file is included from AudioPrimaryHidlTest.h
-// and thus it doesn't have all '#include' and 'using' directives required
-// for a standalone compilation.
+#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 {
@@ -56,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
index c9e0c0d..a94041c 100644
--- a/audio/core/all-versions/vts/functional/PolicyConfig.h
+++ b/audio/core/all-versions/vts/functional/PolicyConfig.h
@@ -16,11 +16,19 @@
 
 #pragma once
 
-// Note: it is assumed that this file is included from AudioPrimaryHidlTest.h
-// and thus it doesn't have all '#include' and 'using' directives required
-// for a standalone compilation.
+#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;
@@ -42,28 +50,14 @@
                 break;
             }
         }
-        mStatus = android::deserializeAudioPolicyFile(mFilePath.c_str(), this);
-        if (mStatus == 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;
-                    }
-                }
-            }
-        }
+        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 {
@@ -88,8 +82,33 @@
     }
 
   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 = NO_INIT;
+    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/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 a5a8b34..f2f5124 100644
--- a/audio/effect/2.0/Android.bp
+++ b/audio/effect/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.effect@2.0",
     root: "android.hardware",
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 31f94ae..1eb754a 100644
--- a/audio/effect/4.0/Android.bp
+++ b/audio/effect/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.audio.effect@4.0",
     root: "android.hardware",
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 a3081c6..126964c 100644
--- a/audio/effect/5.0/Android.bp
+++ b/audio/effect/5.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.effect@5.0",
     root: "android.hardware",
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 de4bde7..8d15d09 100644
--- a/audio/effect/6.0/Android.bp
+++ b/audio/effect/6.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.effect@6.0",
     root: "android.hardware",
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
index c113782..7399cdb 100644
--- a/audio/effect/7.0/Android.bp
+++ b/audio/effect/7.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.effect@7.0",
     root: "android.hardware",
diff --git a/audio/effect/7.0/types.hal b/audio/effect/7.0/types.hal
index c4cb213..8f4f885 100644
--- a/audio/effect/7.0/types.hal
+++ b/audio/effect/7.0/types.hal
@@ -17,6 +17,7 @@
 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,
@@ -219,9 +220,9 @@
      */
     uint16_t memoryUsage;
     /** Human readable effect name. */
-    uint8_t[64] name;
+    string name;
     /** Human readable effect implementor name. */
-    uint8_t[64] implementor;
+    string implementor;
 };
 
 /**
@@ -248,32 +249,19 @@
 };
 
 /**
- * Determines what fields of EffectBufferConfig need to be considered.
- */
-@export(name="", value_prefix="EFFECT_CONFIG_")
-enum EffectConfigParameters : int32_t {
-    /** Buffer field. */
-    BUFFER = 0x0001,
-    /** Sampling rate. */
-    SMP_RATE = 0x0002,
-    /** Channels. */
-    CHANNELS = 0x0004,
-    /** Format. */
-    FORMAT = 0x0008,
-    /** Access mode. */
-    ACC_MODE = 0x0010,
-    // Note that the 2.0 ALL have been moved to an helper function
-};
-
-/**
  * The buffer config structure specifies the input or output audio format
  * to be used by the effect engine.
  */
 struct EffectBufferConfig {
-    AudioBuffer buffer;
-    AudioConfigBase base;
-    EffectBufferAccess accessMode;
-    bitfield<EffectConfigParameters> mask;
+    safe_union OptionalBuffer {
+        Monostate unspecified;
+        AudioBuffer buf;
+    } buffer;
+    AudioConfigBaseOptional base;
+    safe_union OptionalAccessMode {
+        Monostate unspecified;
+        EffectBufferAccess value;
+    } accessMode;
 };
 
 struct EffectConfig {
diff --git a/audio/effect/7.0/xml/Android.bp b/audio/effect/7.0/xml/Android.bp
index dc12e63..978e434 100644
--- a/audio/effect/7.0/xml/Android.bp
+++ b/audio/effect/7.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_V7_0",
     srcs: ["audio_effects_conf.xsd"],
diff --git a/audio/effect/all-versions/default/OWNERS b/audio/effect/all-versions/OWNERS
similarity index 67%
copy from audio/effect/all-versions/default/OWNERS
copy to audio/effect/all-versions/OWNERS
index 6fdc97c..24071af 100644
--- a/audio/effect/all-versions/default/OWNERS
+++ b/audio/effect/all-versions/OWNERS
@@ -1,3 +1,2 @@
 elaurent@google.com
-krocard@google.com
 mnaganov@google.com
diff --git a/audio/effect/all-versions/default/Android.bp b/audio/effect/all-versions/default/Android.bp
index a0cd612..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,6 +59,7 @@
         "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",
@@ -66,6 +75,7 @@
         "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",
@@ -81,6 +91,7 @@
         "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",
@@ -96,6 +107,7 @@
         "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",
@@ -111,6 +123,7 @@
         "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",
diff --git a/audio/effect/all-versions/default/Conversions.cpp b/audio/effect/all-versions/default/Conversions.cpp
deleted file mode 100644
index 0cc8767..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 "UuidUtils.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::UuidUtils;
-
-void 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;
-    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/Effect.cpp b/audio/effect/all-versions/default/Effect.cpp
index edd364c..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"
 
@@ -30,6 +29,7 @@
 #include <HidlUtils.h>
 #include <android/log.h>
 #include <media/EffectsFactoryApi.h>
+#include <util/EffectUtils.h>
 #include <utils/Trace.h>
 
 #include "VersionUtils.h"
@@ -202,34 +202,6 @@
     halConfig->aux_channels = static_cast<audio_channel_mask_t>(config.auxChannels);
 }
 
-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);
-}
-
-// 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);
-}
-
 #else  // MAJOR_VERSION <= 6
 
 void Effect::effectAuxChannelsConfigFromHal(const channel_config_t& halConfig,
@@ -247,50 +219,8 @@
     (void)HidlUtils::audioChannelMaskToHal(config.auxChannels, &halConfig->aux_channels);
 }
 
-void Effect::effectBufferConfigFromHal(const buffer_config_t& halConfig,
-                                       EffectBufferConfig* config) {
-    config->buffer.id = 0;
-    config->buffer.frameCount = 0;
-    audio_config_base_t halConfigBase = {halConfig.samplingRate,
-                                         static_cast<audio_channel_mask_t>(halConfig.channels),
-                                         static_cast<audio_format_t>(halConfig.format)};
-    (void)HidlUtils::audioConfigBaseFromHal(halConfigBase, mIsInput, &config->base);
-    config->accessMode = EffectBufferAccess(halConfig.accessMode);
-    config->mask = static_cast<decltype(config->mask)>(halConfig.mask);
-}
-
-// 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 = nullptr;
-    audio_config_base_t halConfigBase;
-    (void)HidlUtils::audioConfigBaseToHal(config.base, &halConfigBase);
-    halConfig->samplingRate = halConfigBase.sample_rate;
-    halConfig->channels = halConfigBase.channel_mask;
-    halConfig->format = halConfigBase.format;
-    // Note: The framework code does not use BP.
-    halConfig->bufferProvider.cookie = nullptr;
-    halConfig->bufferProvider.getBuffer = nullptr;
-    halConfig->bufferProvider.releaseBuffer = nullptr;
-    halConfig->accessMode = static_cast<uint8_t>(config.accessMode);
-    halConfig->mask = static_cast<uint8_t>(config.mask);
-}
-
 #endif  // MAJOR_VERSION <= 6
 
-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);
-}
-
 // static
 void Effect::effectOffloadParamToHal(const EffectOffloadParameter& offload,
                                      effect_offload_param_t* halOffload) {
@@ -355,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);
 }
@@ -520,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");
     }
@@ -715,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 9aa47ea..d5218f7 100644
--- a/audio/effect/all-versions/default/Effect.h
+++ b/audio/effect/all-versions/default/Effect.h
@@ -203,11 +203,6 @@
                                         EffectAuxChannelsConfig* config);
     static void effectAuxChannelsConfigToHal(const EffectAuxChannelsConfig& config,
                                              channel_config_t* halConfig);
-    void effectBufferConfigFromHal(const buffer_config_t& halConfig, EffectBufferConfig* config);
-    static void effectBufferConfigToHal(const EffectBufferConfig& config,
-                                        buffer_config_t* halConfig);
-    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,
diff --git a/audio/effect/all-versions/default/EffectsFactory.cpp b/audio/effect/all-versions/default/EffectsFactory.cpp
index 1ea990b..eb1cb49 100644
--- a/audio/effect/all-versions/default/EffectsFactory.cpp
+++ b/audio/effect/all-versions/default/EffectsFactory.cpp
@@ -19,7 +19,6 @@
 #include "AcousticEchoCancelerEffect.h"
 #include "AutomaticGainControlEffect.h"
 #include "BassBoostEffect.h"
-#include "Conversions.h"
 #include "DownmixEffect.h"
 #include "Effect.h"
 #include "EnvironmentalReverbEffect.h"
@@ -27,11 +26,11 @@
 #include "LoudnessEnhancerEffect.h"
 #include "NoiseSuppressionEffect.h"
 #include "PresetReverbEffect.h"
-#include "UuidUtils.h"
 #include "VirtualizerEffect.h"
 #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 {
@@ -107,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));
@@ -141,11 +141,11 @@
     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 {
@@ -191,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/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/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 f4a7283..48d6474 100644
--- a/audio/effect/all-versions/vts/functional/Android.bp
+++ b/audio/effect/all-versions/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: "VtsHalAudioEffectTargetTest_default",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
index 35ff869..23e7786 100644
--- a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
@@ -31,7 +31,6 @@
 #include <android/hidl/memory/1.0/IMemory.h>
 #if MAJOR_VERSION >= 7
 #include <android_audio_policy_configuration_V7_0-enums.h>
-#include <android_audio_policy_configuration_V7_0.h>
 #endif
 
 #include <common/all-versions/VersionUtils.h>
@@ -264,8 +263,10 @@
     *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);
+            currentConfig.outputCfg.base.channelMask.value());
     ASSERT_NE(*channelCount, 0);
 #endif
 }
@@ -315,10 +316,10 @@
 std::vector<EffectBufferConfig> generateInvalidConfigs(const EffectBufferConfig& src) {
     std::vector<EffectBufferConfig> result;
     EffectBufferConfig invalidFormat = src;
-    invalidFormat.base.format = "random_string";
+    invalidFormat.base.format.value("random_string");
     result.push_back(std::move(invalidFormat));
     EffectBufferConfig invalidChannelMask = src;
-    invalidChannelMask.base.channelMask = "random_string";
+    invalidChannelMask.base.channelMask.value("random_string");
     result.push_back(std::move(invalidChannelMask));
     return result;
 }
@@ -395,17 +396,22 @@
            rhs.data.handle() == nullptr;
 }
 
+#if MAJOR_VERSION <= 6
 inline bool operator==(const EffectBufferConfig& lhs, const EffectBufferConfig& rhs) {
     return lhs.buffer == rhs.buffer &&
-#if MAJOR_VERSION <= 6
            lhs.samplingRateHz == rhs.samplingRateHz && lhs.channels == rhs.channels &&
            lhs.format == rhs.format &&
-#else
-           lhs.base.sampleRateHz == rhs.base.sampleRateHz &&
-           lhs.base.channelMask == rhs.base.channelMask && lhs.base.format == rhs.base.format &&
-#endif
            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;
diff --git a/audio/policy/1.0/vts/functional/Android.bp b/audio/policy/1.0/vts/functional/Android.bp
index 7b7cf0d..cccb2fc 100644
--- a/audio/policy/1.0/vts/functional/Android.bp
+++ b/audio/policy/1.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: "VtsHalAudioPolicyV1_0TargetTest",
     srcs: [
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 5c556d2..b14284b 100644
--- a/authsecret/1.0/Android.bp
+++ b/authsecret/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.authsecret@1.0",
     root: "android.hardware",
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 687c70c..c09b265 100644
--- a/authsecret/1.0/vts/functional/VtsHalAuthSecretV1_0TargetTest.cpp
+++ b/authsecret/1.0/vts/functional/VtsHalAuthSecretV1_0TargetTest.cpp
@@ -34,11 +34,19 @@
         authsecret = IAuthSecret::getService(GetParam());
         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->primaryUserCredential(CORRECT_SECRET);
     }
 
+    static void TearDownTestSuite() {
+        // clean up PIN code after testing
+        (void)system("cmd lock_settings clear --old 1234");
+    }
+
     sp<IAuthSecret> authsecret;
     hidl_vec<uint8_t> CORRECT_SECRET{61, 93, 124, 240, 5, 0, 7, 201, 9, 129, 11, 12, 0, 14, 0, 16};
     hidl_vec<uint8_t> WRONG_SECRET{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
diff --git a/authsecret/aidl/Android.bp b/authsecret/aidl/Android.bp
index 0a05034..8fe9f27 100644
--- a/authsecret/aidl/Android.bp
+++ b/authsecret/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.authsecret",
     vendor_available: true,
diff --git a/authsecret/aidl/default/Android.bp b/authsecret/aidl/default/Android.bp
index d598344..a6c0bc4 100644
--- a/authsecret/aidl/default/Android.bp
+++ b/authsecret/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.authsecret-service.example",
     relative_install_path: "hw",
@@ -25,7 +34,7 @@
         "AuthSecret.cpp",
     ],
     shared_libs: [
-        "android.hardware.authsecret-ndk_platform",
+        "android.hardware.authsecret-V1-ndk_platform",
         "libbase",
         "libbinder_ndk",
     ],
diff --git a/authsecret/aidl/vts/Android.bp b/authsecret/aidl/vts/Android.bp
index 83a85b2..dca7046 100644
--- a/authsecret/aidl/vts/Android.bp
+++ b/authsecret/aidl/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 {
     name: "VtsHalAuthSecretTargetTest",
     defaults: [
@@ -21,7 +30,7 @@
         "use_libaidlvintf_gtest_helper_static",
     ],
     srcs: ["VtsHalAuthSecretTargetTest.cpp"],
-    static_libs: ["android.hardware.authsecret-ndk_platform"],
+    static_libs: ["android.hardware.authsecret-V1-ndk_platform"],
     shared_libs: ["libbinder_ndk"],
     test_suites: [
         "general-tests",
diff --git a/automotive/audiocontrol/1.0/Android.bp b/automotive/audiocontrol/1.0/Android.bp
index 8835f51..628793b 100644
--- a/automotive/audiocontrol/1.0/Android.bp
+++ b/automotive/audiocontrol/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.automotive.audiocontrol@1.0",
     root: "android.hardware",
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 e9ce638..4d1fdbc 100644
--- a/automotive/audiocontrol/2.0/Android.bp
+++ b/automotive/audiocontrol/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.automotive.audiocontrol@2.0",
     root: "android.hardware",
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 2ddfaf9..47db9f8 100644
--- a/automotive/can/1.0/Android.bp
+++ b/automotive/can/1.0/Android.bp
@@ -1,5 +1,15 @@
 // 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",
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 279c09a..04b9c78 100644
--- a/automotive/evs/1.0/Android.bp
+++ b/automotive/evs/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.automotive.evs@1.0",
     root: "android.hardware",
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.1/Android.bp b/automotive/evs/1.1/Android.bp
index 443422e..a72f971 100644
--- a/automotive/evs/1.1/Android.bp
+++ b/automotive/evs/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.automotive.evs@1.1",
     root: "android.hardware",
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/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
index 8b68fd6..f1c8f9f 100644
--- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
+++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
@@ -72,6 +72,8 @@
 using ::android::hardware::automotive::evs::V1_0::DisplayDesc;
 using ::android::hardware::automotive::evs::V1_0::DisplayState;
 using ::android::hardware::graphics::common::V1_0::PixelFormat;
+using ::android::frameworks::automotive::display::V1_0::HwDisplayConfig;
+using ::android::frameworks::automotive::display::V1_0::HwDisplayState;
 using IEvsCamera_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsCamera;
 using IEvsCamera_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsCamera;
 using IEvsDisplay_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
@@ -303,11 +305,22 @@
             const auto id = 0xFFFFFFFF; // meaningless id
             hidl_vec<uint8_t> values;
             auto err = pCam->setExtendedInfo_1_1(id, values);
-            ASSERT_NE(EvsResult::INVALID_ARG, err);
+            if (isLogicalCam) {
+                // Logical camera device does not support setExtendedInfo
+                // method.
+                ASSERT_EQ(EvsResult::INVALID_ARG, err);
+            } else {
+                ASSERT_NE(EvsResult::INVALID_ARG, err);
+            }
 
-            pCam->getExtendedInfo_1_1(id, [](const auto& result, const auto& data) {
-                ASSERT_NE(EvsResult::INVALID_ARG, result);
-                ASSERT_EQ(0, data.size());
+
+            pCam->getExtendedInfo_1_1(id, [&isLogicalCam](const auto& result, const auto& data) {
+                if (isLogicalCam) {
+                    ASSERT_EQ(EvsResult::INVALID_ARG, result);
+                } else {
+                    ASSERT_NE(EvsResult::INVALID_ARG, result);
+                    ASSERT_EQ(0, data.size());
+                }
             });
 
             // Explicitly close the camera so resources are released right away
@@ -605,7 +618,10 @@
     LOG(INFO) << "Display " << targetDisplayId << " is alreay in use.";
 
     // Get the display descriptor
-    pDisplay->getDisplayInfo_1_1([](const auto& config, const auto& state) {
+    pDisplay->getDisplayInfo_1_1([](const HwDisplayConfig& config, const HwDisplayState& state) {
+        ASSERT_GT(config.size(), 0);
+        ASSERT_GT(state.size(), 0);
+
         android::DisplayConfig* pConfig = (android::DisplayConfig*)config.data();
         const auto width = pConfig->resolution.getWidth();
         const auto height = pConfig->resolution.getHeight();
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 3a39148..a6b1ba7 100644
--- a/automotive/sv/1.0/Android.bp
+++ b/automotive/sv/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.automotive.sv@1.0",
     root: "android.hardware",
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 4fa8773..e2164b1 100644
--- a/automotive/vehicle/2.0/Android.bp
+++ b/automotive/vehicle/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.automotive.vehicle@2.0",
     root: "android.hardware",
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 246246c..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,13 +91,13 @@
         "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"],
     whole_static_libs: [
         "android.hardware.automotive.vehicle@2.0-emulated-user-hal-lib",
         "android.hardware.automotive.vehicle@2.0-manager-lib",
-        "libqemu_pipe",
     ],
     shared_libs: [
         "libbase",
@@ -137,7 +146,6 @@
     local_include_dirs: ["common/include/vhal_v2_0"],
     export_include_dirs: ["impl"],
     srcs: [
-        "impl/vhal_v2_0/EmulatedUserHal.cpp",
         "impl/vhal_v2_0/GeneratorHub.cpp",
         "impl/vhal_v2_0/JsonFakeValueGenerator.cpp",
         "impl/vhal_v2_0/LinearFakeValueGenerator.cpp",
@@ -213,3 +221,29 @@
         "android.hardware.automotive.vehicle@2.0-libproto-native",
     ],
 }
+
+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 47133fd..ef29560 100644
--- a/automotive/vehicle/2.0/default/VehicleService.cpp
+++ b/automotive/vehicle/2.0/default/VehicleService.cpp
@@ -34,7 +34,7 @@
 
 int main(int /* argc */, char* /* argv */ []) {
     auto store = std::make_unique<VehiclePropertyStore>();
-    auto connector = impl::makeEmulatedPassthroughConnector();
+    auto connector = std::make_unique<impl::EmulatedVehicleConnector>();
     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());
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index cf18404..dec2d8c 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -417,7 +417,7 @@
                          .minSampleRate = 1.0f,
                          .maxSampleRate = 2.0f,
                  },
-         .initialValue = {.floatValues = {100.0f}}},  // units in meters
+         .initialValue = {.floatValues = {50000.0f}}},  // units in meters
 
         {.config =
                  {
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp
index 7f9362f..ed3f4a2 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp
@@ -35,13 +35,33 @@
 
 namespace impl {
 
-class EmulatedPassthroughConnector : public PassthroughConnector {
-  public:
-    bool onDump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
-};
+EmulatedUserHal* EmulatedVehicleConnector::getEmulatedUserHal() {
+    return &mEmulatedUserHal;
+}
 
-bool EmulatedPassthroughConnector::onDump(const hidl_handle& handle,
-                                          const hidl_vec<hidl_string>& options) {
+StatusCode EmulatedVehicleConnector::onSetProperty(const VehiclePropValue& value,
+                                                   bool updateStatus) {
+    if (mEmulatedUserHal.isSupported(value.prop)) {
+        LOG(INFO) << "onSetProperty(): property " << value.prop << " will be handled by UserHal";
+
+        const auto& ret = mEmulatedUserHal.onSetProperty(value);
+        if (!ret.ok()) {
+            LOG(ERROR) << "onSetProperty(): HAL returned error: " << ret.error().message();
+            return StatusCode(ret.error().code());
+        }
+        auto updatedValue = ret.value().get();
+        if (updatedValue != nullptr) {
+            LOG(INFO) << "onSetProperty(): updating property returned by HAL: "
+                      << toString(*updatedValue);
+            onPropertyValueFromCar(*updatedValue, updateStatus);
+        }
+        return StatusCode::OK;
+    }
+    return this->VehicleHalServer::onSetProperty(value, updateStatus);
+}
+
+bool EmulatedVehicleConnector::onDump(const hidl_handle& handle,
+                                      const hidl_vec<hidl_string>& options) {
     int fd = handle->data[0];
 
     if (options.size() > 0) {
@@ -68,10 +88,6 @@
     return true;
 }
 
-PassthroughConnectorPtr makeEmulatedPassthroughConnector() {
-    return std::make_unique<EmulatedPassthroughConnector>();
-}
-
 }  // namespace impl
 
 }  // namespace V2_0
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h
index 57cbb8b..4c6c661 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h
@@ -19,6 +19,7 @@
 
 #include <vhal_v2_0/VehicleConnector.h>
 
+#include "EmulatedUserHal.h"
 #include "VehicleHalClient.h"
 #include "VehicleHalServer.h"
 
@@ -30,10 +31,20 @@
 
 namespace impl {
 
-using PassthroughConnector = IPassThroughConnector<VehicleHalClient, VehicleHalServer>;
-using PassthroughConnectorPtr = std::unique_ptr<PassthroughConnector>;
+class EmulatedVehicleConnector : public IPassThroughConnector<VehicleHalClient, VehicleHalServer> {
+  public:
+    EmulatedVehicleConnector() {}
 
-PassthroughConnectorPtr makeEmulatedPassthroughConnector();
+    EmulatedUserHal* getEmulatedUserHal();
+
+    // Methods from VehicleHalServer
+    StatusCode onSetProperty(const VehiclePropValue& value, bool updateStatus) override;
+
+    bool onDump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
+
+  private:
+    EmulatedUserHal mEmulatedUserHal;
+};
 
 }  // namespace impl
 
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 a0b566d..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
@@ -272,17 +272,6 @@
     return false;
 }
 
-// determine if it's running inside Android Emulator
-static bool isInEmulator() {
-    char propValue[PROP_VALUE_MAX];
-    bool isEmulator = (__system_property_get("ro.kernel.qemu", propValue) != 0);
-    if (!isEmulator) {
-        isEmulator = (__system_property_get("ro.hardware", propValue) != 0) &&
-                     (!strcmp(propValue, "ranchu") || !strcmp(propValue, "goldfish"));
-    }
-    return isEmulator;
-}
-
 // Parse supported properties list and generate vector of property values to hold current values.
 void EmulatedVehicleHal::onCreate() {
     static constexpr bool shouldUpdateStatus = true;
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 890eb33..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
@@ -96,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;
     }
 
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/VehicleHalServer.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.cpp
index 36f2534..0ee1835 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.cpp
@@ -41,10 +41,6 @@
     return mValuePool;
 }
 
-EmulatedUserHal* VehicleHalServer::getEmulatedUserHal() {
-    return &mEmulatedUserHal;
-}
-
 void VehicleHalServer::setValuePool(VehiclePropValuePool* valuePool) {
     if (!valuePool) {
         LOG(WARNING) << __func__ << ": Setting value pool to nullptr!";
@@ -185,22 +181,6 @@
 }
 
 StatusCode VehicleHalServer::onSetProperty(const VehiclePropValue& value, bool updateStatus) {
-    if (mEmulatedUserHal.isSupported(value.prop)) {
-        LOG(INFO) << "onSetProperty(): property " << value.prop << " will be handled by UserHal";
-
-        const auto& ret = mEmulatedUserHal.onSetProperty(value);
-        if (!ret.ok()) {
-            LOG(ERROR) << "onSetProperty(): HAL returned error: " << ret.error().message();
-            return StatusCode(ret.error().code());
-        }
-        auto updatedValue = ret.value().get();
-        if (updatedValue != nullptr) {
-            LOG(INFO) << "onSetProperty(): updating property returned by HAL: "
-                      << toString(*updatedValue);
-            onPropertyValueFromCar(*updatedValue, updateStatus);
-        }
-        return StatusCode::OK;
-    }
     LOG(DEBUG) << "onSetProperty(" << value.prop << ")";
 
     // Some properties need to be treated non-trivially
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.h
index fca78bc..117eadb 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.h
@@ -19,7 +19,6 @@
 #include <vhal_v2_0/VehicleObjectPool.h>
 #include <vhal_v2_0/VehicleServer.h>
 
-#include "EmulatedUserHal.h"
 #include "GeneratorHub.h"
 
 namespace android::hardware::automotive::vehicle::V2_0::impl {
@@ -38,8 +37,6 @@
     // Set the Property Value Pool used in this server
     void setValuePool(VehiclePropValuePool* valuePool);
 
-    EmulatedUserHal* getEmulatedUserHal();
-
   private:
     using VehiclePropValuePtr = recyclable_ptr<VehiclePropValue>;
 
@@ -56,11 +53,6 @@
     VehiclePropValuePtr createHwInputKeyProp(VehicleHwKeyInputAction action, int32_t keyCode,
                                              int32_t targetDisplay);
 
-    // data members
-
-  protected:
-    EmulatedUserHal mEmulatedUserHal;
-
   private:
     GeneratorHub mGeneratorHub{
             std::bind(&VehicleHalServer::onFakeValueGenerated, this, std::placeholders::_1)};
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 dd406f9..d3c262a 100644
--- a/biometrics/face/1.0/Android.bp
+++ b/biometrics/face/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.biometrics.face@1.0",
     root: "android.hardware",
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/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/fingerprint/2.1/Android.bp b/biometrics/fingerprint/2.1/Android.bp
index 25bd48d..3378930 100644
--- a/biometrics/fingerprint/2.1/Android.bp
+++ b/biometrics/fingerprint/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.biometrics.fingerprint@2.1",
     root: "android.hardware",
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/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.2/Android.bp b/biometrics/fingerprint/2.2/Android.bp
index a8f202c..bea8d44 100644
--- a/biometrics/fingerprint/2.2/Android.bp
+++ b/biometrics/fingerprint/2.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.biometrics.fingerprint@2.2",
     root: "android.hardware",
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 1cac820..8d023c0 100644
--- a/bluetooth/1.0/Android.bp
+++ b/bluetooth/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.bluetooth@1.0",
     root: "android.hardware",
diff --git a/bluetooth/1.0/default/Android.bp b/bluetooth/1.0/default/Android.bp
index 6e39d54..70a42b7 100644
--- a/bluetooth/1.0/default/Android.bp
+++ b/bluetooth/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.bluetooth@1.0-impl",
     defaults: ["hidl_defaults"],
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 0328af1..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;
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 c3967f0..4feb28b 100644
--- a/bluetooth/1.1/Android.bp
+++ b/bluetooth/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.bluetooth@1.1",
     root: "android.hardware",
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.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 d9ec982..7f39d4f 100644
--- a/bluetooth/a2dp/1.0/Android.bp
+++ b/bluetooth/a2dp/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.bluetooth.a2dp@1.0",
     root: "android.hardware",
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/audio/2.0/Android.bp b/bluetooth/audio/2.0/Android.bp
index 3fbd51f..dd39f9f 100644
--- a/bluetooth/audio/2.0/Android.bp
+++ b/bluetooth/audio/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.bluetooth.audio@2.0",
     root: "android.hardware",
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 50119bf..0000000
--- a/bluetooth/audio/2.0/default/session/BluetoothAudioSession.cpp
+++ /dev/null
@@ -1,429 +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_);
-  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;
-}
-
-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/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.1/Android.bp b/bluetooth/audio/2.1/Android.bp
index 9af8add..822f5b3 100644
--- a/bluetooth/audio/2.1/Android.bp
+++ b/bluetooth/audio/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.bluetooth.audio@2.1",
     root: "android.hardware",
diff --git a/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.cpp b/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.cpp
index b4a61b6..3fe1a4d 100644
--- a/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.cpp
+++ b/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.cpp
@@ -22,8 +22,8 @@
 #include <fmq/MessageQueue.h>
 #include <hidl/MQDescriptor.h>
 
-#include "BluetoothAudioSessionReport.h"
-#include "BluetoothAudioSupportedCodecsDB.h"
+#include "BluetoothAudioSessionReport_2_1.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
 
 namespace android {
 namespace hardware {
@@ -32,7 +32,7 @@
 namespace V2_1 {
 namespace implementation {
 
-using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
 using ::android::hardware::kSynchronizedReadWrite;
 using ::android::hardware::MessageQueue;
 using ::android::hardware::Void;
@@ -81,8 +81,8 @@
 
 Return<void> A2dpOffloadAudioProvider::onSessionReady(
     startSession_cb _hidl_cb) {
-  BluetoothAudioSessionReport::OnSessionStarted(session_type_, stack_iface_,
-                                                nullptr, audio_config_);
+  BluetoothAudioSessionReport_2_1::OnSessionStarted(session_type_, stack_iface_,
+                                                    nullptr, audio_config_);
   _hidl_cb(BluetoothAudioStatus::SUCCESS, DataMQ::Descriptor());
   return Void();
 }
diff --git a/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.cpp b/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.cpp
index a67c341..a37176b 100644
--- a/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.cpp
+++ b/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.cpp
@@ -20,8 +20,8 @@
 
 #include <android-base/logging.h>
 
-#include "BluetoothAudioSessionReport.h"
-#include "BluetoothAudioSupportedCodecsDB.h"
+#include "BluetoothAudioSessionReport_2_1.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
 
 namespace android {
 namespace hardware {
@@ -30,7 +30,7 @@
 namespace V2_1 {
 namespace implementation {
 
-using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
 using ::android::hardware::Void;
 using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
 
@@ -96,7 +96,7 @@
 Return<void> A2dpSoftwareAudioProvider::onSessionReady(
     startSession_cb _hidl_cb) {
   if (mDataMQ && mDataMQ->isValid()) {
-    BluetoothAudioSessionReport::OnSessionStarted(
+    BluetoothAudioSessionReport_2_1::OnSessionStarted(
         session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
     _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
   } else {
diff --git a/bluetooth/audio/2.1/default/Android.bp b/bluetooth/audio/2.1/default/Android.bp
index 5381fec..5c30f79 100644
--- a/bluetooth/audio/2.1/default/Android.bp
+++ b/bluetooth/audio/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_library_shared {
     name: "android.hardware.bluetooth.audio@2.1-impl",
     defaults: ["hidl_defaults"],
@@ -16,29 +25,7 @@
         "android.hardware.bluetooth.audio@2.0",
         "android.hardware.bluetooth.audio@2.1",
         "libbase",
-        "libbluetooth_audio_session_2_1",
-        "libcutils",
-        "libfmq",
-        "libhidlbase",
-        "liblog",
-        "libutils",
-    ],
-}
-
-cc_library_shared {
-    name: "libbluetooth_audio_session_2_1",
-    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",
-        "android.hardware.bluetooth.audio@2.1",
-        "libbase",
+        "libbluetooth_audio_session",
         "libcutils",
         "libfmq",
         "libhidlbase",
diff --git a/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp b/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
index 73fe06c..38889ae 100644
--- a/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
+++ b/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
@@ -20,8 +20,8 @@
 
 #include <android-base/logging.h>
 
-#include "BluetoothAudioSessionReport.h"
-#include "BluetoothAudioSupportedCodecsDB.h"
+#include "BluetoothAudioSessionReport_2_1.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
 
 namespace android {
 namespace hardware {
@@ -30,7 +30,7 @@
 namespace V2_1 {
 namespace implementation {
 
-using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
 using ::android::hardware::kSynchronizedReadWrite;
 using ::android::hardware::MessageQueue;
 using ::android::hardware::Void;
@@ -105,8 +105,8 @@
    * HAL server should start the streaming on data path.
    */
   if (stack_iface_) {
-    BluetoothAudioSessionReport::ReportControlStatus(session_type_, true,
-                                                     status);
+    BluetoothAudioSessionReport_2_1::ReportControlStatus(session_type_, true,
+                                                         status);
   } else {
     LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
                  << ", status=" << toString(status) << " has NO session";
@@ -125,8 +125,8 @@
    * HAL server should suspend the streaming on data path.
    */
   if (stack_iface_) {
-    BluetoothAudioSessionReport::ReportControlStatus(session_type_, false,
-                                                     status);
+    BluetoothAudioSessionReport_2_1::ReportControlStatus(session_type_, false,
+                                                         status);
   } else {
     LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
                  << ", status=" << toString(status) << " has NO session";
@@ -139,7 +139,7 @@
   LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
 
   if (stack_iface_) {
-    BluetoothAudioSessionReport::OnSessionEnded(session_type_);
+    BluetoothAudioSessionReport_2_1::OnSessionEnded(session_type_);
     stack_iface_->unlinkToDeath(death_recipient_);
   } else {
     LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
diff --git a/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.cpp b/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.cpp
index adf2717..e1b1ac6 100644
--- a/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.cpp
+++ b/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.cpp
@@ -20,7 +20,7 @@
 
 #include <android-base/logging.h>
 
-#include "BluetoothAudioSupportedCodecsDB.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
 
 namespace android {
 namespace hardware {
diff --git a/bluetooth/audio/2.1/default/HearingAidAudioProvider.cpp b/bluetooth/audio/2.1/default/HearingAidAudioProvider.cpp
index aded7e1..712bd4f 100644
--- a/bluetooth/audio/2.1/default/HearingAidAudioProvider.cpp
+++ b/bluetooth/audio/2.1/default/HearingAidAudioProvider.cpp
@@ -20,8 +20,8 @@
 
 #include <android-base/logging.h>
 
-#include "BluetoothAudioSessionReport.h"
-#include "BluetoothAudioSupportedCodecsDB.h"
+#include "BluetoothAudioSessionReport_2_1.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
 
 namespace android {
 namespace hardware {
@@ -30,7 +30,7 @@
 namespace V2_1 {
 namespace implementation {
 
-using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
 using ::android::hardware::Void;
 using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
 
@@ -95,7 +95,7 @@
 
 Return<void> HearingAidAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
   if (mDataMQ && mDataMQ->isValid()) {
-    BluetoothAudioSessionReport::OnSessionStarted(
+    BluetoothAudioSessionReport_2_1::OnSessionStarted(
         session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
     _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
   } else {
diff --git a/bluetooth/audio/2.1/default/LeAudioAudioProvider.cpp b/bluetooth/audio/2.1/default/LeAudioAudioProvider.cpp
index 9c2b4fe..2ebf6c5 100644
--- a/bluetooth/audio/2.1/default/LeAudioAudioProvider.cpp
+++ b/bluetooth/audio/2.1/default/LeAudioAudioProvider.cpp
@@ -21,8 +21,8 @@
 
 #include <android-base/logging.h>
 
-#include "BluetoothAudioSessionReport.h"
-#include "BluetoothAudioSupportedCodecsDB.h"
+#include "BluetoothAudioSessionReport_2_1.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
 
 namespace android {
 namespace hardware {
@@ -31,7 +31,7 @@
 namespace V2_1 {
 namespace implementation {
 
-using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+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;
@@ -179,7 +179,7 @@
 
 Return<void> LeAudioAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
   if (mDataMQ && mDataMQ->isValid()) {
-    BluetoothAudioSessionReport::OnSessionStarted(
+    BluetoothAudioSessionReport_2_1::OnSessionStarted(
         session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
     _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
   } else {
diff --git a/bluetooth/audio/2.1/default/session/BluetoothAudioSession.h b/bluetooth/audio/2.1/default/session/BluetoothAudioSession.h
deleted file mode 100644
index 7bc12e6..0000000
--- a/bluetooth/audio/2.1/default/session/BluetoothAudioSession.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * 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 <mutex>
-#include <unordered_map>
-
-#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioPort.h>
-#include <android/hardware/bluetooth/audio/2.1/types.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::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_1::AudioConfiguration;
-using ::android::hardware::bluetooth::audio::V2_1::PcmParameters;
-using ::android::hardware::bluetooth::audio::V2_1::SampleRate;
-using ::android::hardware::bluetooth::audio::V2_1::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 again.
-  std::recursive_mutex mutex_;
-  SessionType session_type_;
-
-  // audio control path to use for both software and offloading
-  sp<IBluetoothAudioPort> stack_iface_;
-  // Audio path (FMQ) for software encoding/decoded data
-  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,
-      .dataIntervalUs = 0,
-  };
-  // 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
-  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.1/default/session/BluetoothAudioSessionControl.h b/bluetooth/audio/2.1/default/session/BluetoothAudioSessionControl.h
deleted file mode 100644
index 017a611..0000000
--- a/bluetooth/audio/2.1/default/session/BluetoothAudioSessionControl.h
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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 "BluetoothAudioSession.h"
-
-namespace android {
-namespace bluetooth {
-namespace audio {
-
-class BluetoothAudioSessionControl {
- 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& session_type) {
-    std::shared_ptr<BluetoothAudioSession> session_ptr =
-        BluetoothAudioSessionInstance::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& session_type, const PortStatusCallbacks& cbacks) {
-    std::shared_ptr<BluetoothAudioSession> session_ptr =
-        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
-    if (session_ptr != nullptr) {
-      return session_ptr->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& session_type,
-                                           uint16_t cookie) {
-    std::shared_ptr<BluetoothAudioSession> session_ptr =
-        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
-    if (session_ptr != nullptr) {
-      session_ptr->UnregisterStatusCback(cookie);
-    }
-  }
-
-  // The control API for the bluetooth_audio module to get current
-  // AudioConfiguration
-  static const AudioConfiguration& GetAudioConfig(
-      const SessionType& session_type) {
-    std::shared_ptr<BluetoothAudioSession> session_ptr =
-        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
-    if (session_ptr != nullptr) {
-      return session_ptr->GetAudioConfig();
-    } else if (session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
-      return BluetoothAudioSession::kInvalidOffloadAudioConfiguration;
-    } else {
-      return BluetoothAudioSession::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& session_type) {
-    std::shared_ptr<BluetoothAudioSession> session_ptr =
-        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
-    if (session_ptr != nullptr) {
-      return session_ptr->StartStream();
-    }
-    return false;
-  }
-
-  static bool SuspendStream(const SessionType& session_type) {
-    std::shared_ptr<BluetoothAudioSession> session_ptr =
-        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
-    if (session_ptr != nullptr) {
-      return session_ptr->SuspendStream();
-    }
-    return false;
-  }
-
-  static void StopStream(const SessionType& session_type) {
-    std::shared_ptr<BluetoothAudioSession> session_ptr =
-        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
-    if (session_ptr != nullptr) {
-      session_ptr->StopStream();
-    }
-  }
-
-  static bool GetPresentationPosition(const SessionType& session_type,
-                                      uint64_t* remote_delay_report_ns,
-                                      uint64_t* total_bytes_readed,
-                                      timespec* data_position) {
-    std::shared_ptr<BluetoothAudioSession> session_ptr =
-        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
-    if (session_ptr != nullptr) {
-      return session_ptr->GetPresentationPosition(
-          remote_delay_report_ns, total_bytes_readed, data_position);
-    }
-    return false;
-  }
-
-  static void UpdateTracksMetadata(
-      const SessionType& session_type,
-      const struct source_metadata* source_metadata) {
-    std::shared_ptr<BluetoothAudioSession> session_ptr =
-        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
-    if (session_ptr != nullptr) {
-      session_ptr->UpdateTracksMetadata(source_metadata);
-    }
-  }
-
-  // The control API writes stream to FMQ
-  static size_t OutWritePcmData(const SessionType& session_type,
-                                const void* buffer, size_t bytes) {
-    std::shared_ptr<BluetoothAudioSession> session_ptr =
-        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
-    if (session_ptr != nullptr) {
-      return session_ptr->OutWritePcmData(buffer, bytes);
-    }
-    return 0;
-  }
-
-  // The control API reads stream from FMQ
-  static size_t InReadPcmData(const SessionType& session_type, void* buffer,
-                              size_t bytes) {
-    std::shared_ptr<BluetoothAudioSession> session_ptr =
-        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
-    if (session_ptr != nullptr) {
-      return session_ptr->InReadPcmData(buffer, bytes);
-    }
-    return 0;
-  }
-};
-
-}  // namespace audio
-}  // namespace bluetooth
-}  // namespace android
diff --git a/bluetooth/audio/2.1/default/session/BluetoothAudioSessionReport.h b/bluetooth/audio/2.1/default/session/BluetoothAudioSessionReport.h
deleted file mode 100644
index 267bf8f..0000000
--- a/bluetooth/audio/2.1/default/session/BluetoothAudioSessionReport.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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 "BluetoothAudioSession.h"
-
-namespace android {
-namespace bluetooth {
-namespace audio {
-
-class BluetoothAudioSessionReport {
- public:
-  // The API reports the Bluetooth stack has started the session, and will
-  // inform registered bluetooth_audio session
-  static void OnSessionStarted(const SessionType& session_type,
-                               const sp<IBluetoothAudioPort> host_iface,
-                               const DataMQ::Descriptor* dataMQ,
-                               const AudioConfiguration& audio_config) {
-    std::shared_ptr<BluetoothAudioSession> session_ptr =
-        BluetoothAudioSessionInstance::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 SessionType& session_type) {
-    std::shared_ptr<BluetoothAudioSession> session_ptr =
-        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
-    if (session_ptr != nullptr) {
-      session_ptr->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 SessionType& session_type,
-                                  const bool& start_resp,
-                                  const BluetoothAudioStatus& status) {
-    std::shared_ptr<BluetoothAudioSession> session_ptr =
-        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
-    if (session_ptr != nullptr) {
-      session_ptr->ReportControlStatus(start_resp, status);
-    }
-  }
-};
-
-}  // namespace audio
-}  // namespace bluetooth
-}  // namespace android
diff --git a/bluetooth/audio/2.1/default/session/BluetoothAudioSupportedCodecsDB.cpp b/bluetooth/audio/2.1/default/session/BluetoothAudioSupportedCodecsDB.cpp
deleted file mode 100644
index 0937f44..0000000
--- a/bluetooth/audio/2.1/default/session/BluetoothAudioSupportedCodecsDB.cpp
+++ /dev/null
@@ -1,489 +0,0 @@
-/*
- * 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 "BTAudioProviderSessionCodecsDB"
-
-#include "BluetoothAudioSupportedCodecsDB.h"
-
-#include <android-base/logging.h>
-
-namespace android {
-namespace bluetooth {
-namespace audio {
-
-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::BitsPerSample;
-using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
-using ::android::hardware::bluetooth::audio::V2_0::CodecType;
-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::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_1::SampleRate;
-
-// Default Supported PCM Parameters
-static const ::android::hardware::bluetooth::audio::V2_0::PcmParameters
-    kDefaultSoftwarePcmCapabilities = {
-        .sampleRate = static_cast<
-            android::hardware::bluetooth::audio::V2_0::SampleRate>(
-            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 |
-            android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_16000),
-        .channelMode =
-            static_cast<ChannelMode>(ChannelMode::MONO | ChannelMode::STEREO),
-        .bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16 |
-                                                    BitsPerSample::BITS_24 |
-                                                    BitsPerSample::BITS_32)};
-
-static const PcmParameters kDefaultSoftwarePcmCapabilities_2_1 = {
-    .sampleRate = static_cast<SampleRate>(
-        SampleRate::RATE_48000 | SampleRate::RATE_44100 |
-        SampleRate::RATE_32000 | SampleRate::RATE_24000 |
-        SampleRate::RATE_16000 | SampleRate::RATE_8000),
-    .channelMode =
-        static_cast<ChannelMode>(ChannelMode::MONO | ChannelMode::STEREO),
-    .bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16)};
-
-// Default Supported Codecs
-// SBC: mSampleRate:(44100), mBitsPerSample:(16), mChannelMode:(MONO|STEREO)
-//      all blocks | subbands 8 | Loudness
-static const SbcParameters kDefaultOffloadSbcCapability = {
-    .sampleRate =
-        android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100,
-    .channelMode = static_cast<SbcChannelMode>(SbcChannelMode::MONO |
-                                               SbcChannelMode::JOINT_STEREO),
-    .blockLength = static_cast<SbcBlockLength>(
-        SbcBlockLength::BLOCKS_4 | SbcBlockLength::BLOCKS_8 |
-        SbcBlockLength::BLOCKS_12 | SbcBlockLength::BLOCKS_16),
-    .numSubbands = SbcNumSubbands::SUBBAND_8,
-    .allocMethod = SbcAllocMethod::ALLOC_MD_L,
-    .bitsPerSample = BitsPerSample::BITS_16,
-    .minBitpool = 2,
-    .maxBitpool = 53};
-
-// AAC: mSampleRate:(44100), mBitsPerSample:(16), mChannelMode:(STEREO)
-static const AacParameters kDefaultOffloadAacCapability = {
-    .objectType = AacObjectType::MPEG2_LC,
-    .sampleRate =
-        android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100,
-    .channelMode = ChannelMode::STEREO,
-    .variableBitRateEnabled = AacVariableBitRate::ENABLED,
-    .bitsPerSample = BitsPerSample::BITS_16};
-
-// LDAC: mSampleRate:(44100|48000|88200|96000), mBitsPerSample:(16|24|32),
-//       mChannelMode:(DUAL|STEREO)
-static const LdacParameters kDefaultOffloadLdacCapability = {
-    .sampleRate =
-        static_cast<android::hardware::bluetooth::audio::V2_0::SampleRate>(
-            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),
-    .channelMode = static_cast<LdacChannelMode>(LdacChannelMode::DUAL |
-                                                LdacChannelMode::STEREO),
-    .qualityIndex = LdacQualityIndex::QUALITY_HIGH,
-    .bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16 |
-                                                BitsPerSample::BITS_24 |
-                                                BitsPerSample::BITS_32)};
-
-// aptX: mSampleRate:(44100|48000), mBitsPerSample:(16), mChannelMode:(STEREO)
-static const AptxParameters kDefaultOffloadAptxCapability = {
-    .sampleRate =
-        static_cast<android::hardware::bluetooth::audio::V2_0::SampleRate>(
-            android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100 |
-            android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000),
-    .channelMode = ChannelMode::STEREO,
-    .bitsPerSample = BitsPerSample::BITS_16,
-};
-
-// aptX HD: mSampleRate:(44100|48000), mBitsPerSample:(24),
-//          mChannelMode:(STEREO)
-static const AptxParameters kDefaultOffloadAptxHdCapability = {
-    .sampleRate =
-        static_cast<android::hardware::bluetooth::audio::V2_0::SampleRate>(
-            android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100 |
-            android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000),
-    .channelMode = ChannelMode::STEREO,
-    .bitsPerSample = BitsPerSample::BITS_24,
-};
-
-const std::vector<CodecCapabilities> kDefaultOffloadA2dpCodecCapabilities = {
-    {.codecType = CodecType::SBC, .capabilities = {}},
-    {.codecType = CodecType::AAC, .capabilities = {}},
-    {.codecType = CodecType::LDAC, .capabilities = {}},
-    {.codecType = CodecType::APTX, .capabilities = {}},
-    {.codecType = CodecType::APTX_HD, .capabilities = {}}};
-
-static bool IsSingleBit(uint32_t bitmasks, uint32_t bitfield) {
-  bool single = false;
-  uint32_t test_bit = 0x00000001;
-  while (test_bit <= bitmasks && test_bit <= bitfield) {
-    if (bitfield & test_bit && bitmasks & test_bit) {
-      if (single) return false;
-      single = true;
-    }
-    if (test_bit == 0x80000000) break;
-    test_bit <<= 1;
-  }
-  return single;
-}
-
-static bool IsOffloadSbcConfigurationValid(
-    const CodecConfiguration::CodecSpecific& codec_specific);
-static bool IsOffloadAacConfigurationValid(
-    const CodecConfiguration::CodecSpecific& codec_specific);
-static bool IsOffloadLdacConfigurationValid(
-    const CodecConfiguration::CodecSpecific& codec_specific);
-static bool IsOffloadAptxConfigurationValid(
-    const CodecConfiguration::CodecSpecific& codec_specific);
-static bool IsOffloadAptxHdConfigurationValid(
-    const CodecConfiguration::CodecSpecific& codec_specific);
-
-static bool IsOffloadSbcConfigurationValid(
-    const CodecConfiguration::CodecSpecific& codec_specific) {
-  if (codec_specific.getDiscriminator() !=
-      CodecConfiguration::CodecSpecific::hidl_discriminator::sbcConfig) {
-    LOG(WARNING) << __func__
-                 << ": Invalid CodecSpecific=" << toString(codec_specific);
-    return false;
-  }
-  const SbcParameters sbc_data = codec_specific.sbcConfig();
-  if (!IsSingleBit(static_cast<uint32_t>(sbc_data.sampleRate), 0xff) ||
-      !IsSingleBit(static_cast<uint32_t>(sbc_data.channelMode), 0x0f) ||
-      !IsSingleBit(static_cast<uint32_t>(sbc_data.blockLength), 0xf0) ||
-      !IsSingleBit(static_cast<uint32_t>(sbc_data.numSubbands), 0x0c) ||
-      !IsSingleBit(static_cast<uint32_t>(sbc_data.allocMethod), 0x03) ||
-      !IsSingleBit(static_cast<uint32_t>(sbc_data.bitsPerSample), 0x07) ||
-      sbc_data.minBitpool > sbc_data.maxBitpool) {
-    LOG(WARNING) << __func__
-                 << ": Invalid CodecSpecific=" << toString(codec_specific);
-    return false;
-  } else if ((sbc_data.sampleRate & kDefaultOffloadSbcCapability.sampleRate) &&
-             (sbc_data.channelMode &
-              kDefaultOffloadSbcCapability.channelMode) &&
-             (sbc_data.blockLength &
-              kDefaultOffloadSbcCapability.blockLength) &&
-             (sbc_data.numSubbands &
-              kDefaultOffloadSbcCapability.numSubbands) &&
-             (sbc_data.allocMethod &
-              kDefaultOffloadSbcCapability.allocMethod) &&
-             (sbc_data.bitsPerSample &
-              kDefaultOffloadSbcCapability.bitsPerSample) &&
-             (kDefaultOffloadSbcCapability.minBitpool <= sbc_data.minBitpool &&
-              sbc_data.maxBitpool <= kDefaultOffloadSbcCapability.maxBitpool)) {
-    return true;
-  }
-  LOG(WARNING) << __func__
-               << ": Unsupported CodecSpecific=" << toString(codec_specific);
-  return false;
-}
-
-static bool IsOffloadAacConfigurationValid(
-    const CodecConfiguration::CodecSpecific& codec_specific) {
-  if (codec_specific.getDiscriminator() !=
-      CodecConfiguration::CodecSpecific::hidl_discriminator::aacConfig) {
-    LOG(WARNING) << __func__
-                 << ": Invalid CodecSpecific=" << toString(codec_specific);
-    return false;
-  }
-  const AacParameters aac_data = codec_specific.aacConfig();
-  if (!IsSingleBit(static_cast<uint32_t>(aac_data.objectType), 0xf0) ||
-      !IsSingleBit(static_cast<uint32_t>(aac_data.sampleRate), 0xff) ||
-      !IsSingleBit(static_cast<uint32_t>(aac_data.channelMode), 0x03) ||
-      !IsSingleBit(static_cast<uint32_t>(aac_data.bitsPerSample), 0x07)) {
-    LOG(WARNING) << __func__
-                 << ": Invalid CodecSpecific=" << toString(codec_specific);
-    return false;
-  } else if ((aac_data.objectType & kDefaultOffloadAacCapability.objectType) &&
-             (aac_data.sampleRate & kDefaultOffloadAacCapability.sampleRate) &&
-             (aac_data.channelMode &
-              kDefaultOffloadAacCapability.channelMode) &&
-             (aac_data.variableBitRateEnabled == AacVariableBitRate::DISABLED ||
-              kDefaultOffloadAacCapability.variableBitRateEnabled ==
-                  AacVariableBitRate::ENABLED) &&
-             (aac_data.bitsPerSample &
-              kDefaultOffloadAacCapability.bitsPerSample)) {
-    return true;
-  }
-  LOG(WARNING) << __func__
-               << ": Unsupported CodecSpecific=" << toString(codec_specific);
-  return false;
-}
-
-static bool IsOffloadLdacConfigurationValid(
-    const CodecConfiguration::CodecSpecific& codec_specific) {
-  if (codec_specific.getDiscriminator() !=
-      CodecConfiguration::CodecSpecific::hidl_discriminator::ldacConfig) {
-    LOG(WARNING) << __func__
-                 << ": Invalid CodecSpecific=" << toString(codec_specific);
-    return false;
-  }
-  const LdacParameters ldac_data = codec_specific.ldacConfig();
-  if (!IsSingleBit(static_cast<uint32_t>(ldac_data.sampleRate), 0xff) ||
-      !IsSingleBit(static_cast<uint32_t>(ldac_data.channelMode), 0x07) ||
-      (ldac_data.qualityIndex > LdacQualityIndex::QUALITY_LOW &&
-       ldac_data.qualityIndex != LdacQualityIndex::QUALITY_ABR) ||
-      !IsSingleBit(static_cast<uint32_t>(ldac_data.bitsPerSample), 0x07)) {
-    LOG(WARNING) << __func__
-                 << ": Invalid CodecSpecific=" << toString(codec_specific);
-    return false;
-  } else if ((ldac_data.sampleRate &
-              kDefaultOffloadLdacCapability.sampleRate) &&
-             (ldac_data.channelMode &
-              kDefaultOffloadLdacCapability.channelMode) &&
-             (ldac_data.bitsPerSample &
-              kDefaultOffloadLdacCapability.bitsPerSample)) {
-    return true;
-  }
-  LOG(WARNING) << __func__
-               << ": Unsupported CodecSpecific=" << toString(codec_specific);
-  return false;
-}
-
-static bool IsOffloadAptxConfigurationValid(
-    const CodecConfiguration::CodecSpecific& codec_specific) {
-  if (codec_specific.getDiscriminator() !=
-      CodecConfiguration::CodecSpecific::hidl_discriminator::aptxConfig) {
-    LOG(WARNING) << __func__
-                 << ": Invalid CodecSpecific=" << toString(codec_specific);
-    return false;
-  }
-  const AptxParameters aptx_data = codec_specific.aptxConfig();
-  if (!IsSingleBit(static_cast<uint32_t>(aptx_data.sampleRate), 0xff) ||
-      !IsSingleBit(static_cast<uint32_t>(aptx_data.channelMode), 0x03) ||
-      !IsSingleBit(static_cast<uint32_t>(aptx_data.bitsPerSample), 0x07)) {
-    LOG(WARNING) << __func__
-                 << ": Invalid CodecSpecific=" << toString(codec_specific);
-    return false;
-  } else if ((aptx_data.sampleRate &
-              kDefaultOffloadAptxCapability.sampleRate) &&
-             (aptx_data.channelMode &
-              kDefaultOffloadAptxCapability.channelMode) &&
-             (aptx_data.bitsPerSample &
-              kDefaultOffloadAptxCapability.bitsPerSample)) {
-    return true;
-  }
-  LOG(WARNING) << __func__
-               << ": Unsupported CodecSpecific=" << toString(codec_specific);
-  return false;
-}
-
-static bool IsOffloadAptxHdConfigurationValid(
-    const CodecConfiguration::CodecSpecific& codec_specific) {
-  if (codec_specific.getDiscriminator() !=
-      CodecConfiguration::CodecSpecific::hidl_discriminator::aptxConfig) {
-    LOG(WARNING) << __func__
-                 << ": Invalid CodecSpecific=" << toString(codec_specific);
-    return false;
-  }
-  const AptxParameters aptx_data = codec_specific.aptxConfig();
-  if (!IsSingleBit(static_cast<uint32_t>(aptx_data.sampleRate), 0xff) ||
-      !IsSingleBit(static_cast<uint32_t>(aptx_data.channelMode), 0x03) ||
-      !IsSingleBit(static_cast<uint32_t>(aptx_data.bitsPerSample), 0x07)) {
-    LOG(WARNING) << __func__
-                 << ": Invalid CodecSpecific=" << toString(codec_specific);
-    return false;
-  } else if ((aptx_data.sampleRate &
-              kDefaultOffloadAptxHdCapability.sampleRate) &&
-             (aptx_data.channelMode &
-              kDefaultOffloadAptxHdCapability.channelMode) &&
-             (aptx_data.bitsPerSample &
-              kDefaultOffloadAptxHdCapability.bitsPerSample)) {
-    return true;
-  }
-  LOG(WARNING) << __func__
-               << ": Unsupported CodecSpecific=" << toString(codec_specific);
-  return false;
-}
-
-std::vector<::android::hardware::bluetooth::audio::V2_0::PcmParameters>
-GetSoftwarePcmCapabilities() {
-  return std::vector<
-      ::android::hardware::bluetooth::audio::V2_0::PcmParameters>(
-      1, kDefaultSoftwarePcmCapabilities);
-}
-
-std::vector<PcmParameters> GetSoftwarePcmCapabilities_2_1() {
-  return std::vector<PcmParameters>(1, kDefaultSoftwarePcmCapabilities_2_1);
-}
-
-std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
-    const ::android::hardware::bluetooth::audio::V2_0::SessionType&
-        session_type) {
-  return GetOffloadCodecCapabilities(static_cast<SessionType>(session_type));
-}
-
-std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
-    const SessionType& session_type) {
-  if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
-    return std::vector<CodecCapabilities>(0);
-  }
-  std::vector<CodecCapabilities> offload_a2dp_codec_capabilities =
-      kDefaultOffloadA2dpCodecCapabilities;
-  for (auto& codec_capability : offload_a2dp_codec_capabilities) {
-    switch (codec_capability.codecType) {
-      case CodecType::SBC:
-        codec_capability.capabilities.sbcCapabilities(
-            kDefaultOffloadSbcCapability);
-        break;
-      case CodecType::AAC:
-        codec_capability.capabilities.aacCapabilities(
-            kDefaultOffloadAacCapability);
-        break;
-      case CodecType::LDAC:
-        codec_capability.capabilities.ldacCapabilities(
-            kDefaultOffloadLdacCapability);
-        break;
-      case CodecType::APTX:
-        codec_capability.capabilities.aptxCapabilities(
-            kDefaultOffloadAptxCapability);
-        break;
-      case CodecType::APTX_HD:
-        codec_capability.capabilities.aptxCapabilities(
-            kDefaultOffloadAptxHdCapability);
-        break;
-      case CodecType::UNKNOWN:
-        codec_capability = {};
-        break;
-    }
-  }
-  return offload_a2dp_codec_capabilities;
-}
-
-bool IsSoftwarePcmConfigurationValid(
-    const ::android::hardware::bluetooth::audio::V2_0::PcmParameters&
-        pcm_config) {
-  if ((pcm_config.sampleRate !=
-           android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100 &&
-       pcm_config.sampleRate !=
-           android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000 &&
-       pcm_config.sampleRate !=
-           android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_88200 &&
-       pcm_config.sampleRate !=
-           android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_96000 &&
-       pcm_config.sampleRate !=
-           android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_16000 &&
-       pcm_config.sampleRate !=
-           android::hardware::bluetooth::audio::V2_0::SampleRate::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.sampleRate &&
-             pcm_config.bitsPerSample &
-                 kDefaultSoftwarePcmCapabilities.bitsPerSample &&
-             pcm_config.channelMode &
-                 kDefaultSoftwarePcmCapabilities.channelMode) {
-    return true;
-  }
-  LOG(WARNING) << __func__
-               << ": Unsupported PCM Configuration=" << toString(pcm_config);
-  return false;
-}
-
-bool IsSoftwarePcmConfigurationValid_2_1(const PcmParameters& pcm_config) {
-  if ((pcm_config.sampleRate != SampleRate::RATE_96000 &&
-       pcm_config.sampleRate != SampleRate::RATE_88200 &&
-       pcm_config.sampleRate != SampleRate::RATE_48000 &&
-       pcm_config.sampleRate != SampleRate::RATE_44100 &&
-       pcm_config.sampleRate != SampleRate::RATE_32000 &&
-       pcm_config.sampleRate != SampleRate::RATE_24000 &&
-       pcm_config.sampleRate != SampleRate::RATE_16000 &&
-       pcm_config.sampleRate != SampleRate::RATE_8000) ||
-      (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 SessionType& session_type,
-                                      const CodecConfiguration& codec_config) {
-  if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
-    LOG(ERROR) << __func__
-               << ": Invalid SessionType=" << toString(session_type);
-    return false;
-  } else if (codec_config.encodedAudioBitrate < 0x00000001 ||
-             0x00ffffff < codec_config.encodedAudioBitrate) {
-    LOG(ERROR) << __func__ << ": Unsupported Codec Configuration="
-               << toString(codec_config);
-    return false;
-  }
-  const CodecConfiguration::CodecSpecific& codec_specific = codec_config.config;
-  switch (codec_config.codecType) {
-    case CodecType::SBC:
-      if (IsOffloadSbcConfigurationValid(codec_specific)) {
-        return true;
-      }
-      return false;
-    case CodecType::AAC:
-      if (IsOffloadAacConfigurationValid(codec_specific)) {
-        return true;
-      }
-      return false;
-    case CodecType::LDAC:
-      if (IsOffloadLdacConfigurationValid(codec_specific)) {
-        return true;
-      }
-      return false;
-    case CodecType::APTX:
-      if (IsOffloadAptxConfigurationValid(codec_specific)) {
-        return true;
-      }
-      return false;
-    case CodecType::APTX_HD:
-      if (IsOffloadAptxHdConfigurationValid(codec_specific)) {
-        return true;
-      }
-      return false;
-    case CodecType::UNKNOWN:
-      return false;
-  }
-  return false;
-}
-
-}  // namespace audio
-}  // namespace bluetooth
-}  // namespace android
diff --git a/bluetooth/audio/2.1/default/session/BluetoothAudioSupportedCodecsDB.h b/bluetooth/audio/2.1/default/session/BluetoothAudioSupportedCodecsDB.h
deleted file mode 100644
index 9b2f680..0000000
--- a/bluetooth/audio/2.1/default/session/BluetoothAudioSupportedCodecsDB.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.0/types.h>
-#include <android/hardware/bluetooth/audio/2.1/types.h>
-
-namespace android {
-namespace bluetooth {
-namespace audio {
-
-using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
-using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
-using ::android::hardware::bluetooth::audio::V2_1::PcmParameters;
-using ::android::hardware::bluetooth::audio::V2_1::SessionType;
-
-std::vector<::android::hardware::bluetooth::audio::V2_0::PcmParameters>
-GetSoftwarePcmCapabilities();
-std::vector<PcmParameters> GetSoftwarePcmCapabilities_2_1();
-std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
-    const SessionType& session_type);
-std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
-    const ::android::hardware::bluetooth::audio::V2_0::SessionType&
-        session_type);
-
-bool IsSoftwarePcmConfigurationValid_2_1(const PcmParameters& pcm_config);
-bool IsSoftwarePcmConfigurationValid(
-    const ::android::hardware::bluetooth::audio::V2_0::PcmParameters&
-        pcm_config);
-bool IsOffloadCodecConfigurationValid(const SessionType& session_type,
-                                      const CodecConfiguration& codec_config);
-
-}  // namespace audio
-}  // namespace bluetooth
-}  // namespace android
diff --git a/bluetooth/audio/2.1/types.hal b/bluetooth/audio/2.1/types.hal
index 5604c38..e0dcc02 100644
--- a/bluetooth/audio/2.1/types.hal
+++ b/bluetooth/audio/2.1/types.hal
@@ -16,13 +16,14 @@
 
 package android.hardware.bluetooth.audio@2.1;
 
-import @2.0::PcmParameters;
-import @2.0::SessionType;
-import @2.0::SampleRate;
-import @2.0::ChannelMode;
 import @2.0::BitsPerSample;
-import @2.0::CodecConfiguration;
+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 */
@@ -35,6 +36,10 @@
     LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH,
 };
 
+enum CodecType : @2.0::CodecType {
+    LC3 = 0x20,
+};
+
 enum SampleRate : @2.0::SampleRate {
     RATE_8000 = 0x100,
     RATE_32000 = 0x200,
@@ -49,14 +54,57 @@
     uint32_t dataIntervalUs;
 };
 
-/** Used to configure either a Hardware or Software Encoding session based on session type */
-safe_union AudioConfiguration {
-    PcmParameters pcmConfig;
-    CodecConfiguration codecConfig;
+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/functional/Android.bp b/bluetooth/audio/2.1/vts/functional/Android.bp
index 6ec5537..3314a8a 100644
--- a/bluetooth/audio/2.1/vts/functional/Android.bp
+++ b/bluetooth/audio/2.1/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_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp b/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
index 95903d1..57fa07b 100644
--- a/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
+++ b/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
@@ -1032,7 +1032,7 @@
  * stopped with different PCM config
  */
 TEST_P(BluetoothAudioProviderLeAudioOutputSoftwareHidlTest,
-       StartAndEndLeAudioOutputSessionWithPossiblePcmConfig) {
+       DISABLED_StartAndEndLeAudioOutputSessionWithPossiblePcmConfig) {
   bool is_codec_config_valid;
   std::unique_ptr<DataMQ> tempDataMQ;
   auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
@@ -1126,7 +1126,7 @@
  * stopped with different PCM config
  */
 TEST_P(BluetoothAudioProviderLeAudioInputSoftwareHidlTest,
-       StartAndEndLeAudioInputSessionWithPossiblePcmConfig) {
+       DISABLED_StartAndEndLeAudioInputSessionWithPossiblePcmConfig) {
   bool is_codec_config_valid;
   std::unique_ptr<DataMQ> tempDataMQ;
   auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
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/2.1/default/session/BluetoothAudioSession.cpp b/bluetooth/audio/utils/session/BluetoothAudioSession.cpp
similarity index 96%
rename from bluetooth/audio/2.1/default/session/BluetoothAudioSession.cpp
rename to bluetooth/audio/utils/session/BluetoothAudioSession.cpp
index ea2c54a..2f3ddaf 100644
--- a/bluetooth/audio/2.1/default/session/BluetoothAudioSession.cpp
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -44,9 +44,9 @@
 
 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
+    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),
@@ -176,9 +176,7 @@
     const AudioConfiguration& audio_config) {
   bool is_software_session =
       (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
-       session_type_ == SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH ||
-       session_type_ == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
-       session_type_ == SessionType::LE_AUDIO_SOFTWARE_DECODED_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();
@@ -390,7 +388,7 @@
 
       if (!mDataMQ->write(static_cast<const uint8_t*>(buffer) + totalWritten,
                           availableToWrite)) {
-        ALOGE("FMQ datapath writing %zu/%zu failed", totalWritten, bytes);
+        ALOGE("FMQ datapath writting %zu/%zu failed", totalWritten, bytes);
         return totalWritten;
       }
       totalWritten += availableToWrite;
@@ -399,7 +397,7 @@
       usleep(kWritePollMs * 1000);
       ms_timeout -= kWritePollMs;
     } else {
-      ALOGD("out data %zu/%zu overflow %d ms", totalWritten, bytes,
+      ALOGD("data %zu/%zu overflow %d ms", totalWritten, bytes,
             (kFmqSendTimeoutMs - ms_timeout));
       return totalWritten;
     }
@@ -445,7 +443,7 @@
         std::unique_ptr<BluetoothAudioSessionInstance>(
             new BluetoothAudioSessionInstance());
 
-// API to fetch the session
+// API to fetch the session of A2DP / Hearing Aid
 std::shared_ptr<BluetoothAudioSession>
 BluetoothAudioSessionInstance::GetSessionInstance(
     const SessionType& session_type) {
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSession.h b/bluetooth/audio/utils/session/BluetoothAudioSession.h
similarity index 97%
rename from bluetooth/audio/2.0/default/session/BluetoothAudioSession.h
rename to bluetooth/audio/utils/session/BluetoothAudioSession.h
index 838d1cc..83e20ad 100644
--- a/bluetooth/audio/2.0/default/session/BluetoothAudioSession.h
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession.h
@@ -79,6 +79,8 @@
 };
 
 class BluetoothAudioSession {
+  friend class BluetoothAudioSession_2_1;
+
  private:
   // using recursive_mutex to allow hwbinder to re-enter agian.
   std::recursive_mutex mutex_;
@@ -153,6 +155,8 @@
 
   // 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,
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..4d7be21
--- /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->GetAudioSession()->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..9d91196
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_1.cpp
@@ -0,0 +1,207 @@
+/*
+ * 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);
+  }
+}
+
+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 (audio_session->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_session =
+      (session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_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_offload_audio_config =
+      (is_offload_session &&
+       audio_config_discriminator ==
+           ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
+               hidl_discriminator::codecConfig);
+  if (!is_software_audio_config && !is_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..5a35153
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_1.h
@@ -0,0 +1,94 @@
+/*
+ * 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);
+
+  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..8b0b0f7
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.cpp
@@ -0,0 +1,127 @@
+/*
+ * 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;
+}
+
+}  // 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..746d9c0
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.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.
+ */
+
+#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);
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace android
diff --git a/boot/1.0/Android.bp b/boot/1.0/Android.bp
index 844cf9b..8117066 100644
--- a/boot/1.0/Android.bp
+++ b/boot/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.boot@1.0",
     root: "android.hardware",
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.1/Android.bp b/boot/1.1/Android.bp
index 3f505e6..a2a8447 100644
--- a/boot/1.1/Android.bp
+++ b/boot/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.boot@1.1",
     root: "android.hardware",
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/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.2/Android.bp b/boot/1.2/Android.bp
index e51c5cd..17ba3e9 100644
--- a/boot/1.2/Android.bp
+++ b/boot/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.boot@1.2",
     root: "android.hardware",
diff --git a/boot/1.2/default/Android.bp b/boot/1.2/default/Android.bp
index c097667..4e1c35e 100644
--- a/boot/1.2/default/Android.bp
+++ b/boot/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_library_shared {
     name: "android.hardware.boot@1.2-impl",
     stem: "android.hardware.boot@1.0-impl-1.2",
diff --git a/boot/1.2/vts/functional/Android.bp b/boot/1.2/vts/functional/Android.bp
index a7f5ccb..7d546ed 100644
--- a/boot/1.2/vts/functional/Android.bp
+++ b/boot/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: "VtsHalBootV1_2TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/broadcastradio/1.0/Android.bp b/broadcastradio/1.0/Android.bp
index 5fc120d..fca4ac8 100644
--- a/broadcastradio/1.0/Android.bp
+++ b/broadcastradio/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.broadcastradio@1.0",
     root: "android.hardware",
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.1/Android.bp b/broadcastradio/1.1/Android.bp
index 5efa3d4..2874a7d 100644
--- a/broadcastradio/1.1/Android.bp
+++ b/broadcastradio/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.broadcastradio@1.1",
     root: "android.hardware",
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/2.0/Android.bp b/broadcastradio/2.0/Android.bp
index 0ef635e..447b2c7 100644
--- a/broadcastradio/2.0/Android.bp
+++ b/broadcastradio/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.broadcastradio@2.0",
     root: "android.hardware",
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 ce50f25..362ab41 100644
--- a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
+++ b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
@@ -486,6 +486,50 @@
 }
 
 /**
+ * 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) {
+    ASSERT_TRUE(openSession());
+
+    ProgramSelector sel = {};
+    uint64_t freq = 178352;
+    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 +560,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 +615,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();
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 bd00dbb..3eb12b9 100644
--- a/camera/common/1.0/Android.bp
+++ b/camera/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.camera.common@1.0",
     root: "android.hardware",
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 f2125af..6947779 100644
--- a/camera/device/1.0/Android.bp
+++ b/camera/device/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.camera.device@1.0",
     root: "android.hardware",
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/3.2/Android.bp b/camera/device/3.2/Android.bp
index 93d1e75..c80538c 100644
--- a/camera/device/3.2/Android.bp
+++ b/camera/device/3.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.camera.device@3.2",
     root: "android.hardware",
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.3/Android.bp b/camera/device/3.3/Android.bp
index 0f8502b..f5e51d6 100644
--- a/camera/device/3.3/Android.bp
+++ b/camera/device/3.3/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.camera.device@3.3",
     root: "android.hardware",
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 5575366..2a6faab 100644
--- a/camera/device/3.4/Android.bp
+++ b/camera/device/3.4/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.camera.device@3.4",
     root: "android.hardware",
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.5/Android.bp b/camera/device/3.5/Android.bp
index 9496216..f29f936 100644
--- a/camera/device/3.5/Android.bp
+++ b/camera/device/3.5/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.camera.device@3.5",
     root: "android.hardware",
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 e6f458c..ff37ca3 100644
--- a/camera/device/3.6/Android.bp
+++ b/camera/device/3.6/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.camera.device@3.6",
     root: "android.hardware",
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 6e55139..ec8107e 100644
--- a/camera/metadata/3.2/Android.bp
+++ b/camera/metadata/3.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.camera.metadata@3.2",
     root: "android.hardware",
diff --git a/camera/metadata/3.3/Android.bp b/camera/metadata/3.3/Android.bp
index f11fe2b..4bed25b 100644
--- a/camera/metadata/3.3/Android.bp
+++ b/camera/metadata/3.3/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.camera.metadata@3.3",
     root: "android.hardware",
diff --git a/camera/metadata/3.4/Android.bp b/camera/metadata/3.4/Android.bp
index 31218be..fdddfdf 100644
--- a/camera/metadata/3.4/Android.bp
+++ b/camera/metadata/3.4/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.camera.metadata@3.4",
     root: "android.hardware",
diff --git a/camera/metadata/3.5/Android.bp b/camera/metadata/3.5/Android.bp
index a28ba43..9349d54 100644
--- a/camera/metadata/3.5/Android.bp
+++ b/camera/metadata/3.5/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.camera.metadata@3.5",
     root: "android.hardware",
diff --git a/camera/provider/2.4/Android.bp b/camera/provider/2.4/Android.bp
index 8b67f3f..a4c0dd2 100644
--- a/camera/provider/2.4/Android.bp
+++ b/camera/provider/2.4/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.camera.provider@2.4",
     root: "android.hardware",
diff --git a/camera/provider/2.4/default/Android.bp b/camera/provider/2.4/default/Android.bp
index bbb21c7..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"],
diff --git a/camera/provider/2.4/default/service.cpp b/camera/provider/2.4/default/service.cpp
index 84f4839..0a4f787 100644
--- a/camera/provider/2.4/default/service.cpp
+++ b/camera/provider/2.4/default/service.cpp
@@ -22,9 +22,9 @@
 
 #include <android/hardware/camera/provider/2.4/ICameraProvider.h>
 #include <binder/ProcessState.h>
-#include <cutils/memory.h>
 #include <cutils/properties.h>
 #include <hidl/LegacySupport.h>
+#include <malloc.h>
 
 using android::status_t;
 using android::hardware::defaultLazyPassthroughServiceImplementation;
@@ -46,7 +46,13 @@
 
     // b/166675194
     if (property_get_bool("ro.vendor.camera.provider24.disable_mem_init", false)) {
-        process_disable_memory_mitigations();
+        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;
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 2099dc0..7e6b5ad 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -888,6 +888,8 @@
     static Status getSystemCameraKind(const camera_metadata_t* staticMeta,
                                       SystemCameraKind* systemCameraKind);
 
+    static V3_2::DataspaceFlags getDataspace(PixelFormat format);
+
     void processCaptureRequestInternal(uint64_t bufferusage, RequestTemplate reqTemplate,
                                        bool useSecureOnlyCameras);
 
@@ -3178,7 +3180,6 @@
     }
 }
 
-
 // Verify that all supported stream formats and sizes can be configured
 // successfully.
 TEST_P(CameraHidlTest, configureStreamsAvailableOutputs) {
@@ -3221,17 +3222,7 @@
         uint32_t streamConfigCounter = 0;
         for (auto& it : outputStreams) {
             V3_2::Stream stream3_2;
-            V3_2::DataspaceFlags dataspaceFlag = 0;
-            switch (static_cast<PixelFormat>(it.format)) {
-                case PixelFormat::BLOB:
-                    dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::V0_JFIF);
-                    break;
-                case PixelFormat::Y16:
-                    dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::DEPTH);
-                    break;
-                default:
-                    dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::UNKNOWN);
-            }
+            V3_2::DataspaceFlags dataspaceFlag = getDataspace(static_cast<PixelFormat>(it.format));
             stream3_2 = {streamId,
                              StreamType::OUTPUT,
                              static_cast<uint32_t>(it.width),
@@ -3351,17 +3342,8 @@
             size_t j = 0;
             for (const auto& it : outputStreams) {
                 V3_2::Stream stream3_2;
-                V3_2::DataspaceFlags dataspaceFlag = 0;
-                switch (static_cast<PixelFormat>(it.format)) {
-                    case PixelFormat::BLOB:
-                        dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::V0_JFIF);
-                        break;
-                    case PixelFormat::Y16:
-                        dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::DEPTH);
-                        break;
-                    default:
-                        dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::UNKNOWN);
-                }
+                V3_2::DataspaceFlags dataspaceFlag = getDataspace(
+                        static_cast<PixelFormat>(it.format));
                 stream3_2 = {streamId++,
                              StreamType::OUTPUT,
                              static_cast<uint32_t>(it.width),
@@ -5901,6 +5883,23 @@
     return ret;
 }
 
+// Select an appropriate dataspace given a specific pixel format.
+V3_2::DataspaceFlags CameraHidlTest::getDataspace(PixelFormat format) {
+    switch (format) {
+        case PixelFormat::BLOB:
+            return static_cast<V3_2::DataspaceFlags>(Dataspace::V0_JFIF);
+        case PixelFormat::Y16:
+            return static_cast<V3_2::DataspaceFlags>(Dataspace::DEPTH);
+        case PixelFormat::RAW16:
+        case PixelFormat::RAW_OPAQUE:
+        case PixelFormat::RAW10:
+        case PixelFormat::RAW12:
+            return  static_cast<V3_2::DataspaceFlags>(Dataspace::ARBITRARY);
+        default:
+            return static_cast<V3_2::DataspaceFlags>(Dataspace::UNKNOWN);
+    }
+}
+
 // Check whether this is a monochrome camera using the static camera characteristics.
 Status CameraHidlTest::isMonochromeCamera(const camera_metadata_t *staticMeta) {
     Status ret = Status::METHOD_NOT_SUPPORTED;
@@ -6275,17 +6274,8 @@
     ASSERT_EQ(Status::OK, rc);
     ASSERT_FALSE(outputStreams.empty());
 
-    V3_2::DataspaceFlags dataspaceFlag = 0;
-    switch (static_cast<PixelFormat>(outputStreams[idx].format)) {
-        case PixelFormat::BLOB:
-            dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::V0_JFIF);
-            break;
-        case PixelFormat::Y16:
-            dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::DEPTH);
-            break;
-        default:
-            dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::UNKNOWN);
-    }
+    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 be71806..e14c0a8 100644
--- a/camera/provider/2.5/Android.bp
+++ b/camera/provider/2.5/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.camera.provider@2.5",
     root: "android.hardware",
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 6934c17..f402a56 100644
--- a/camera/provider/2.6/Android.bp
+++ b/camera/provider/2.6/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.camera.provider@2.6",
     root: "android.hardware",
diff --git a/cas/1.0/Android.bp b/cas/1.0/Android.bp
index 9f289a1..368156a 100644
--- a/cas/1.0/Android.bp
+++ b/cas/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.cas@1.0",
     root: "android.hardware",
diff --git a/cas/1.0/default/Android.bp b/cas/1.0/default/Android.bp
index 802dce1..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"],
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 e20298b..f5b1cc9 100644
--- a/cas/1.1/Android.bp
+++ b/cas/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.cas@1.1",
     root: "android.hardware",
diff --git a/cas/1.1/default/Android.bp b/cas/1.1/default/Android.bp
index dc42a42..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"],
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 f03b6b7..6c94020 100644
--- a/cas/1.2/Android.bp
+++ b/cas/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.cas@1.2",
     root: "android.hardware",
diff --git a/cas/1.2/default/Android.bp b/cas/1.2/default/Android.bp
index 94d5b3d..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"],
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 6aa4204..3bb74c1 100644
--- a/cas/native/1.0/Android.bp
+++ b/cas/native/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.cas.native@1.0",
     root: "android.hardware",
diff --git a/common/aidl/Android.bp b/common/aidl/Android.bp
index 8029efa..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,
@@ -22,6 +31,7 @@
             apex_available: [
                 "//apex_available:platform",
                 "com.android.media.swcodec",
+                "com.android.neuralnetworks",
             ],
             min_sdk_version: "29",
         },
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl b/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/Ashmem.aidl
similarity index 60%
copy from security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl
copy to common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/Ashmem.aidl
index cef8eca..a438031 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl
+++ b/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/Ashmem.aidl
@@ -1,3 +1,18 @@
+/*
+ * 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -16,8 +31,9 @@
 // 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;
+package android.hardware.common;
 @VintfStability
-parcelable ByteArray {
-  byte[] data;
+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/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/common/aidl/android/hardware/common/Ashmem.aidl
similarity index 60%
copy from security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
copy to common/aidl/android/hardware/common/Ashmem.aidl
index c3b402e..8e40266 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
+++ b/common/aidl/android/hardware/common/Ashmem.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,13 +14,21 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
+package android.hardware.common;
+
+import android.os.ParcelFileDescriptor;
 
 /**
- * This is used to contain a byte[], to make out parameters of byte arrays
- * more convenient for callers.
+ * Type that holds same memory as the "ashmem" hidl_memory type from HIDL.
  */
 @VintfStability
-parcelable ByteArray {
-    byte[] data;
+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
index 9389712..1ab724f 100644
--- a/common/fmq/aidl/Android.bp
+++ b/common/fmq/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.fmq",
     host_supported: true,
diff --git a/common/support/Android.bp b/common/support/Android.bp
index 3bb4804..730798d 100644
--- a/common/support/Android.bp
+++ b/common/support/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: "libaidlcommonsupport",
     vendor_available: true,
@@ -6,9 +15,14 @@
     srcs: ["NativeHandle.cpp"],
     export_include_dirs: ["include"],
     shared_libs: [
-        "android.hardware.common-unstable-ndk_platform",
+        "android.hardware.common-V2-ndk_platform",
         "libcutils",
     ],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.neuralnetworks",
+    ],
+    min_sdk_version: "29",
 }
 
 cc_test {
@@ -17,10 +31,10 @@
     defaults: ["libbinder_ndk_host_user"],
     srcs: ["test.cpp"],
     static_libs: [
+        "android.hardware.common-V2-ndk_platform",
         "libaidlcommonsupport",
     ],
     shared_libs: [
-        "android.hardware.common-unstable-ndk_platform",
         "libcutils",
     ],
     test_suites: ["general-tests"],
diff --git a/compatibility_matrices/Android.bp b/compatibility_matrices/Android.bp
index de73201..314a809 100644
--- a/compatibility_matrices/Android.bp
+++ b/compatibility_matrices/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"],
+}
+
 vintf_compatibility_matrix {
     name: "framework_compatibility_matrix.3.xml",
     stem: "compatibility_matrix.3.xml",
diff --git a/compatibility_matrices/Android.mk b/compatibility_matrices/Android.mk
index 3951666..85c8ca0 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
@@ -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 :=
@@ -104,12 +110,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/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..f1bd0ae 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")
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 5e1266e..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>
@@ -432,7 +432,6 @@
     <hal format="hidl" optional="true">
         <name>android.hardware.tv.cec</name>
         <version>1.0</version>
-        <version>2.0</version>
         <interface>
             <name>IHdmiCec</name>
             <instance>default</instance>
diff --git a/compatibility_matrices/compatibility_matrix.5.xml b/compatibility_matrices/compatibility_matrix.5.xml
index 8bc663d..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>
@@ -467,7 +474,6 @@
     <hal format="hidl" optional="true">
         <name>android.hardware.tv.cec</name>
         <version>1.0</version>
-        <version>2.0</version>
         <interface>
             <name>IHdmiCec</name>
             <instance>default</instance>
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index a8d9a1b..039ea84 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -9,7 +9,6 @@
     </hal>
     <hal format="hidl" optional="false">
         <name>android.hardware.audio</name>
-        <!-- TODO(b/142480271): remove 6.0 when implemented on reference device. -->
         <version>6.0</version>
         <version>7.0</version>
         <interface>
@@ -19,7 +18,6 @@
     </hal>
     <hal format="hidl" optional="false">
         <name>android.hardware.audio.effect</name>
-        <!-- TODO(b/142480271): remove 6.0 when implemented on reference device. -->
         <version>6.0</version>
         <version>7.0</version>
         <interface>
@@ -75,6 +73,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.automotive.occupant_awareness</name>
+        <version>1</version>
         <interface>
             <name>IOccupantAwareness</name>
             <instance>default</instance>
@@ -84,7 +83,7 @@
         <name>android.hardware.automotive.sv</name>
         <version>1.0</version>
         <interface>
-            <name>ISurroundView</name>
+            <name>ISurroundViewService</name>
             <instance>default</instance>
         </interface>
     </hal>
@@ -98,7 +97,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>
@@ -268,7 +267,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.identity</name>
-        <version>1-2</version>
+        <version>1-3</version>
         <interface>
             <name>IIdentityCredentialStore</name>
             <instance>default</instance>
@@ -298,7 +297,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>
@@ -317,6 +316,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.security.keymint</name>
+        <version>1</version>
         <interface>
             <name>IKeyMintDevice</name>
             <instance>default</instance>
@@ -324,7 +324,15 @@
         </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>
@@ -353,14 +361,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.memtrack</name>
-        <interface>
-            <name>IMemtrack</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.memtrack</name>
-        <version>1.0</version>
+        <version>1</version>
         <interface>
             <name>IMemtrack</name>
             <instance>default</instance>
@@ -374,6 +375,13 @@
             <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>
@@ -392,14 +400,14 @@
     </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="hidl" optional="true">
+    <hal format="aidl" optional="true">
         <name>android.hardware.power.stats</name>
-        <version>1.0</version>
         <interface>
             <name>IPowerStats</name>
             <instance>default</instance>
@@ -452,6 +460,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.rebootescrow</name>
+        <version>1</version>
         <interface>
             <name>IRebootEscrow</name>
             <instance>default</instance>
@@ -468,6 +477,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.security.secureclock</name>
+        <version>1</version>
         <interface>
             <name>ISecureClock</name>
             <instance>default</instance>
@@ -475,6 +485,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.security.sharedsecret</name>
+        <version>1</version>
         <interface>
             <name>ISharedSecret</name>
             <instance>default</instance>
@@ -507,7 +518,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.tetheroffload.control</name>
-        <version>1.0</version>
+        <version>1.1</version>
         <interface>
             <name>IOffloadControl</name>
             <instance>default</instance>
@@ -524,7 +535,6 @@
     <hal format="hidl" optional="true">
         <name>android.hardware.tv.cec</name>
         <version>1.0</version>
-        <version>2.0</version>
         <interface>
             <name>IHdmiCec</name>
             <instance>default</instance>
diff --git a/compatibility_matrices/exclude/Android.bp b/compatibility_matrices/exclude/Android.bp
index d7bf635..9b686ae 100644
--- a/compatibility_matrices/exclude/Android.bp
+++ b/compatibility_matrices/exclude/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_host_static {
     name: "libvintf_fcm_exclude",
     cflags: [
diff --git a/compatibility_matrices/exclude/fcm_exclude.cpp b/compatibility_matrices/exclude/fcm_exclude.cpp
index 849df94..6a4d251 100644
--- a/compatibility_matrices/exclude/fcm_exclude.cpp
+++ b/compatibility_matrices/exclude/fcm_exclude.cpp
@@ -66,7 +66,7 @@
             // TODO(b/171260360) Remove when HAL definition is removed
             "android.hardware.audio.effect@2.0",
             "android.hardware.audio@2.0",
-            // TODO(b/171260613) Remove when HAL definition is removed
+            // 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",
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.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 15c4f18..950a55a 100644
--- a/confirmationui/1.0/Android.bp
+++ b/confirmationui/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.confirmationui@1.0",
     root: "android.hardware",
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/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 be30d61..00dc666 100644
--- a/contexthub/1.0/Android.bp
+++ b/contexthub/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.contexthub@1.0",
     root: "android.hardware",
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/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 9ee8e76..6aba5f7 100644
--- a/contexthub/1.1/Android.bp
+++ b/contexthub/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.contexthub@1.1",
     root: "android.hardware",
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 1d80797..0b6f698 100644
--- a/current.txt
+++ b/current.txt
@@ -775,14 +775,63 @@
 6017b4f2481feb0fffceae81c62bc372c898998b2d8fe69fbd39859d3a315e5e android.hardware.keymaster@4.0::IKeymasterDevice
 dabe23dde7c9e3ad65c61def7392f186d7efe7f4216f9b6f9cf0863745b1a9f4 android.hardware.keymaster@4.1::IKeymasterDevice
 cd84ab19c590e0e73dd2307b591a3093ee18147ef95e6d5418644463a6620076 android.hardware.neuralnetworks@1.2::IDevice
-9625e85f56515ad2cf87b6a1847906db669f746ea4ab02cd3d4ca25abc9b0109 android.hardware.neuralnetworks@1.2::types
-9e758e208d14f7256e0885d6d8ad0b61121b21d8c313864f981727ae55bffd16 android.hardware.neuralnetworks@1.3::types
+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
 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
-# NOTE: waiting to freeze HALs until later in the release
-# NOTE: new HALs are recommended to be in AIDL
+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
+fe18c9032e4063efca3fff3c377dd69780de1f96e8e2bc3f7d100a5d8bd467b4 android.hardware.tetheroffload.control@1.1::ITetheringOffloadCallback
+e34b4c7bec5e032c14804707ca924dd6b99ed5ba139da7505fe7d698d0fe178f android.hardware.tetheroffload.control@1.1::types
+
+# 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 44fb837..3aa2a4d 100644
--- a/drm/1.0/Android.bp
+++ b/drm/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.drm@1.0",
     root: "android.hardware",
diff --git a/drm/1.0/default/Android.bp b/drm/1.0/default/Android.bp
index 93b3278..af1c076 100644
--- a/drm/1.0/default/Android.bp
+++ b/drm/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_static {
     name: "android.hardware.drm@1.0-helper",
     vendor_available: true,
@@ -46,7 +55,7 @@
 
 android_hardware_drm_1_0_multilib {
     name: "android.hardware.drm@1.0-multilib-lib",
-    compile_multilib: "32",
+    compile_multilib: "prefer32",
     soong_config_variables: {
         TARGET_ENABLE_MEDIADRM_64: {
             compile_multilib: "both",
@@ -56,7 +65,7 @@
 
 android_hardware_drm_1_0_multilib {
     name: "android.hardware.drm@1.0-multilib-exe",
-    compile_multilib: "32",
+    compile_multilib: "prefer32",
     soong_config_variables: {
         TARGET_ENABLE_MEDIADRM_64: {
             compile_multilib: "first",
diff --git a/drm/1.0/default/CryptoPlugin.cpp b/drm/1.0/default/CryptoPlugin.cpp
index 2db3607..e6d4e84 100644
--- a/drm/1.0/default/CryptoPlugin.cpp
+++ b/drm/1.0/default/CryptoPlugin.cpp
@@ -124,7 +124,11 @@
             return Void();
         }
 
-        if (source.offset + offset + source.size > sourceBase->getSize()) {
+        size_t totalSize = 0;
+        if (__builtin_add_overflow(source.offset, offset, &totalSize) ||
+            __builtin_add_overflow(totalSize, source.size, &totalSize) ||
+            totalSize > sourceBase->getSize()) {
+            android_errorWriteLog(0x534e4554, "176496160");
             _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size");
             return Void();
         }
diff --git a/drm/1.0/vts/functional/Android.bp b/drm/1.0/vts/functional/Android.bp
index 0545c70..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"],
diff --git a/drm/1.1/Android.bp b/drm/1.1/Android.bp
index 0af4cf4..3290203 100644
--- a/drm/1.1/Android.bp
+++ b/drm/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.drm@1.1",
     root: "android.hardware",
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.2/Android.bp b/drm/1.2/Android.bp
index f1c60de..453ae0d 100644
--- a/drm/1.2/Android.bp
+++ b/drm/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.drm@1.2",
     root: "android.hardware",
diff --git a/drm/1.2/vts/functional/Android.bp b/drm/1.2/vts/functional/Android.bp
index 271cc04..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"],
diff --git a/drm/1.3/Android.bp b/drm/1.3/Android.bp
index 9a2b9e1..08f7aa8 100644
--- a/drm/1.3/Android.bp
+++ b/drm/1.3/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.drm@1.3",
     root: "android.hardware",
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/dumpstate/1.0/Android.bp b/dumpstate/1.0/Android.bp
index 5d9eefc..4e37ce9 100644
--- a/dumpstate/1.0/Android.bp
+++ b/dumpstate/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.dumpstate@1.0",
     root: "android.hardware",
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.1/Android.bp b/dumpstate/1.1/Android.bp
index 75805df..b87ca91 100644
--- a/dumpstate/1.1/Android.bp
+++ b/dumpstate/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.dumpstate@1.1",
     root: "android.hardware",
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/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/fastboot/1.0/Android.bp b/fastboot/1.0/Android.bp
index 60dfb2d..e097d4b 100644
--- a/fastboot/1.0/Android.bp
+++ b/fastboot/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.fastboot@1.0",
     root: "android.hardware",
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
index 46306cf..f08e10b 100644
--- a/fastboot/1.1/Android.bp
+++ b/fastboot/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.fastboot@1.1",
     root: "android.hardware",
diff --git a/fastboot/1.1/default/Android.bp b/fastboot/1.1/default/Android.bp
index 980586b..f779753 100644
--- a/fastboot/1.1/default/Android.bp
+++ b/fastboot/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_library {
     name: "android.hardware.fastboot@1.1-impl-mock",
     recovery: true,
diff --git a/gatekeeper/1.0/Android.bp b/gatekeeper/1.0/Android.bp
index f5cb8e4..6318816 100644
--- a/gatekeeper/1.0/Android.bp
+++ b/gatekeeper/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.gatekeeper@1.0",
     root: "android.hardware",
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/gnss/1.0/Android.bp b/gnss/1.0/Android.bp
index 22f47e8..873ac05 100644
--- a/gnss/1.0/Android.bp
+++ b/gnss/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.gnss@1.0",
     root: "android.hardware",
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.1/Android.bp b/gnss/1.1/Android.bp
index 1c38e97..0664f48 100644
--- a/gnss/1.1/Android.bp
+++ b/gnss/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.gnss@1.1",
     root: "android.hardware",
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 b7a98b7..e886963 100644
--- a/gnss/2.0/Android.bp
+++ b/gnss/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.gnss@2.0",
     root: "android.hardware",
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.1/Android.bp b/gnss/2.1/Android.bp
index affbeae..6273ecb 100644
--- a/gnss/2.1/Android.bp
+++ b/gnss/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.gnss@2.1",
     root: "android.hardware",
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/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/measurement_corrections/1.0/Android.bp b/gnss/measurement_corrections/1.0/Android.bp
index a140674..d75a9a7 100644
--- a/gnss/measurement_corrections/1.0/Android.bp
+++ b/gnss/measurement_corrections/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.gnss.measurement_corrections@1.0",
     root: "android.hardware",
diff --git a/gnss/measurement_corrections/1.1/Android.bp b/gnss/measurement_corrections/1.1/Android.bp
index 9363848..1ae4ef3 100644
--- a/gnss/measurement_corrections/1.1/Android.bp
+++ b/gnss/measurement_corrections/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.gnss.measurement_corrections@1.1",
     root: "android.hardware",
diff --git a/gnss/visibility_control/1.0/Android.bp b/gnss/visibility_control/1.0/Android.bp
index 975da78..163595a 100644
--- a/gnss/visibility_control/1.0/Android.bp
+++ b/gnss/visibility_control/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.gnss.visibility_control@1.0",
     root: "android.hardware",
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 22e609d..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,
@@ -25,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 2358a8f..2e41208 100644
--- a/graphics/composer/2.1/Android.bp
+++ b/graphics/composer/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.composer@2.1",
     root: "android.hardware",
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/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 dc20eae..6dbd7f5 100644
--- a/graphics/composer/2.1/utils/resources/Android.bp
+++ b/graphics/composer/2.1/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 {
     name: "android.hardware.graphics.composer@2.1-resources",
     defaults: ["hidl_defaults"],
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 9e703d8..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"],
diff --git a/graphics/composer/2.2/Android.bp b/graphics/composer/2.2/Android.bp
index 234b9ac..e44a236 100644
--- a/graphics/composer/2.2/Android.bp
+++ b/graphics/composer/2.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.composer@2.2",
     root: "android.hardware",
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/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 f07a10b..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"],
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.3/Android.bp b/graphics/composer/2.3/Android.bp
index 96f301c..3c52b6f 100644
--- a/graphics/composer/2.3/Android.bp
+++ b/graphics/composer/2.3/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.composer@2.3",
     root: "android.hardware",
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/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 1cbb60e..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"],
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.4/Android.bp b/graphics/composer/2.4/Android.bp
index 2bbe751..e6b238b 100644
--- a/graphics/composer/2.4/Android.bp
+++ b/graphics/composer/2.4/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.composer@2.4",
     root: "android.hardware",
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/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 cab549c..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"],
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 50f282f..46f95dd 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -73,10 +73,15 @@
 
     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;
-    const int32_t mDisplayWidth;
-    const int32_t mDisplayHeight;
+    int32_t mDisplayWidth;
+    int32_t mDisplayHeight;
 };
 
 class GraphicsComposerHidlTest : public ::testing::TestWithParam<std::string> {
@@ -194,6 +199,31 @@
                                        const std::vector<ContentType>& capabilities,
                                        const ContentType& contentType, const char* contentTypeStr);
 
+    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:
     // use the slot count usually set by SF
     static constexpr uint32_t kBufferSlotCount = 64;
@@ -347,7 +377,7 @@
 }
 
 TEST_P(GraphicsComposerHidlTest, getDisplayVsyncPeriod) {
-    for (const auto& display : mDisplays) {
+    for (VtsDisplay& display : mDisplays) {
         for (Config config : mComposerClient->getDisplayConfigs(display.get())) {
             VsyncPeriodNanos expectedVsyncPeriodNanos = mComposerClient->getDisplayAttribute_2_4(
                     display.get(), config,
@@ -358,8 +388,8 @@
 
             constraints.desiredTimeNanos = systemTime();
             constraints.seamlessRequired = false;
-            EXPECT_EQ(Error::NONE, mComposerClient->setActiveConfigWithConstraints(
-                                           display.get(), config, constraints, &timeline));
+            EXPECT_EQ(Error::NONE,
+                      setActiveConfigWithConstraints(display, config, constraints, &timeline));
 
             if (timeline.refreshRequired) {
                 sendRefreshFrame(display, &timeline);
@@ -411,11 +441,10 @@
     constraints.seamlessRequired = false;
     constraints.desiredTimeNanos = systemTime();
 
-    for (const auto& display : mDisplays) {
+    for (VtsDisplay& display : mDisplays) {
         Config invalidConfigId = GetInvalidConfigId(display.get());
         EXPECT_EQ(Error::BAD_CONFIG,
-                  mComposerClient->setActiveConfigWithConstraints(display.get(), invalidConfigId,
-                                                                  constraints, &timeline));
+                  setActiveConfigWithConstraints(display, invalidConfigId, constraints, &timeline));
     }
 }
 
@@ -426,7 +455,7 @@
     constraints.seamlessRequired = true;
     constraints.desiredTimeNanos = systemTime();
 
-    for (const auto& display : mDisplays) {
+    for (VtsDisplay& display : mDisplays) {
         forEachTwoConfigs(display.get(), [&](Config config1, Config config2) {
             const auto configGroup1 = mComposerClient->getDisplayAttribute_2_4(
                     display.get(), config1,
@@ -435,11 +464,10 @@
                     display.get(), config2,
                     IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
             if (configGroup1 != configGroup2) {
-                mComposerClient->setActiveConfig(display.get(), config1);
+                setActiveConfig(display, config1);
                 sendRefreshFrame(display, nullptr);
                 EXPECT_EQ(Error::SEAMLESS_NOT_ALLOWED,
-                          mComposerClient->setActiveConfigWithConstraints(display.get(), config2,
-                                                                          constraints, &timeline));
+                          setActiveConfigWithConstraints(display, config2, constraints, &timeline));
             }
         });
     }
@@ -502,6 +530,8 @@
 
     mWriter->presentDisplay();
     execute();
+
+    ASSERT_NO_FATAL_FAILURE(mComposerClient->destroyLayer(display.get(), layer));
 }
 
 void GraphicsComposerHidlTest::waitForVsyncPeriodChange(Display display,
@@ -523,9 +553,9 @@
 }
 
 void GraphicsComposerHidlTest::Test_setActiveConfigWithConstraints(const TestParameters& params) {
-    for (const auto& display : mDisplays) {
+    for (VtsDisplay& display : mDisplays) {
         forEachTwoConfigs(display.get(), [&](Config config1, Config config2) {
-            mComposerClient->setActiveConfig(display.get(), config1);
+            setActiveConfig(display, config1);
             sendRefreshFrame(display, nullptr);
 
             int32_t vsyncPeriod1 = mComposerClient->getDisplayAttribute_2_4(
@@ -543,8 +573,8 @@
             IComposerClient::VsyncPeriodChangeConstraints constraints = {
                     .desiredTimeNanos = systemTime() + params.delayForChange,
                     .seamlessRequired = false};
-            EXPECT_EQ(Error::NONE, mComposerClient->setActiveConfigWithConstraints(
-                                           display.get(), 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
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/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/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.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/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/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/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 8bda425..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-unstable-ndk_platform",
+        "android.hardware.graphics.common-V2-ndk_platform",
         "android.hardware.graphics.mapper@4.0-vts",
         "libgralloctypes",
         "libsync",
diff --git a/health/1.0/Android.bp b/health/1.0/Android.bp
index 7786c08..003b106 100644
--- a/health/1.0/Android.bp
+++ b/health/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.health@1.0",
     root: "android.hardware",
diff --git a/health/1.0/default/Android.bp b/health/1.0/default/Android.bp
index ff4b875..6a21595 100644
--- a/health/1.0/default/Android.bp
+++ b/health/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.health@1.0-convert",
     vendor_available: true,
diff --git a/health/2.0/Android.bp b/health/2.0/Android.bp
index 420586e..ddd983d 100644
--- a/health/2.0/Android.bp
+++ b/health/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.health@2.0",
     root: "android.hardware",
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.1/Android.bp b/health/2.1/Android.bp
index 80a6501..8a11617 100644
--- a/health/2.1/Android.bp
+++ b/health/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.health@2.1",
     root: "android.hardware",
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/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/storage/1.0/Android.bp b/health/storage/1.0/Android.bp
index b9d892d..70b2bdc 100644
--- a/health/storage/1.0/Android.bp
+++ b/health/storage/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.health.storage@1.0",
     root: "android.hardware",
diff --git a/health/storage/1.0/default/Android.bp b/health/storage/1.0/default/Android.bp
index 3834244..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,
diff --git a/health/storage/1.0/vts/functional/Android.bp b/health/storage/1.0/vts/functional/Android.bp
index 731ad62..ccf22ce 100644
--- a/health/storage/1.0/vts/functional/Android.bp
+++ b/health/storage/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: "VtsHalHealthStorageV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/health/storage/aidl/Android.bp b/health/storage/aidl/Android.bp
index c39a46d..bf49466 100644
--- a/health/storage/aidl/Android.bp
+++ b/health/storage/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.health.storage",
     vendor_available: true,
diff --git a/health/storage/aidl/default/Android.bp b/health/storage/aidl/default/Android.bp
index 68a8ee2..819b885 100644
--- a/health/storage/aidl/default/Android.bp
+++ b/health/storage/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_defaults {
     name: "libhealth_storage_impl_defaults",
     vendor: true,
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.health.storage-unstable-ndk_platform",
+        "android.hardware.health.storage-V1-ndk_platform",
     ],
     static_libs: [
         "libfstab",
diff --git a/health/storage/aidl/vts/functional/Android.bp b/health/storage/aidl/vts/functional/Android.bp
index 86b72a7..be3eac7 100644
--- a/health/storage/aidl/vts/functional/Android.bp
+++ b/health/storage/aidl/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: "VtsHalHealthStorageTargetTest",
     defaults: [
@@ -25,7 +34,7 @@
         "libbinder_ndk",
     ],
     static_libs: [
-        "android.hardware.health.storage-ndk_platform",
+        "android.hardware.health.storage-V1-ndk_platform",
     ],
     header_libs: [
         "libhealth_storage_test_common_headers",
diff --git a/health/storage/impl_common/Android.bp b/health/storage/impl_common/Android.bp
index e1149c0..925cd55 100644
--- a/health/storage/impl_common/Android.bp
+++ b/health/storage/impl_common/Android.bp
@@ -15,6 +15,15 @@
  */
 
 // 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,
diff --git a/health/storage/impl_common/impl_common.cpp b/health/storage/impl_common/impl_common.cpp
index 6e753d4..1a7a7f1 100644
--- a/health/storage/impl_common/impl_common.cpp
+++ b/health/storage/impl_common/impl_common.cpp
@@ -45,32 +45,59 @@
     return "";
 }
 
-Result GarbageCollect(uint64_t timeout_seconds) {
-    std::string path = GetGarbageCollectPath();
+static std::string GetWriteBoosterPath() {
+    Fstab fstab;
+    ReadDefaultFstab(&fstab);
 
-    if (path.empty()) {
+    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 " << path;
+    LOG(INFO) << "Start Dev GC on " << gc_path;
     while (1) {
-        std::string require;
-        if (!ReadFileToString(path, &require)) {
-            PLOG(WARNING) << "Reading manual_gc failed in " << path;
+        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 = Trim(require);
-        if (require == "" || require == "off" || require == "disabled") {
+        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 " << path;
-        if (!WriteStringToFile("1", path)) {
-            PLOG(WARNING) << "Start Dev GC failed on " << path;
+        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;
         }
@@ -81,9 +108,9 @@
         }
         sleep(2);
     }
-    LOG(INFO) << "Stop Dev GC on " << path;
-    if (!WriteStringToFile("0", path)) {
-        PLOG(WARNING) << "Stop Dev GC failed on " << path;
+    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;
     }
 
@@ -97,17 +124,26 @@
     if (path.empty()) {
         output << "Cannot find Dev GC path";
     } else {
-        std::string require;
+        std::string require_gc;
 
-        if (ReadFileToString(path, &require)) {
-            output << path << ":" << require << std::endl;
+        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";
     }
diff --git a/health/storage/test_common/Android.bp b/health/storage/test_common/Android.bp
index 7c6bef4..a43aae5 100644
--- a/health/storage/test_common/Android.bp
+++ b/health/storage/test_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_headers {
     name: "libhealth_storage_test_common_headers",
     export_include_dirs: ["include"],
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..f35f4b7
--- /dev/null
+++ b/identity/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsIdentityTestCases"
+    },
+    {
+      "name": "VtsHalIdentityTargetTest"
+    },
+    {
+      "name": "android.hardware.identity-support-lib-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 7f342d0..7c68aee 100644
--- a/identity/aidl/default/Android.bp
+++ b/identity/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_library_static {
     name: "android.hardware.identity-libeic-hal-common",
     vendor_available: true,
@@ -12,6 +21,7 @@
     cflags: [
         "-Wall",
         "-Wextra",
+        "-Wno-deprecated-declarations",
     ],
     shared_libs: [
         "liblog",
@@ -21,15 +31,16 @@
     ],
     static_libs: [
         "libbase",
-        "libcppbor",
+        "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",
     ],
 }
 
@@ -81,15 +92,16 @@
     ],
     static_libs: [
         "libbase",
-        "libcppbor",
+        "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",
     ],
@@ -97,4 +109,14 @@
         "service.cpp",
         "FakeSecureHardwareProxy.cpp",
     ],
+    required: [
+        "android.hardware.identity_credential.xml",
+    ],
+}
+
+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
index 3f2ec8b..8ec4cc9 100644
--- a/identity/aidl/default/EicOpsImpl.cc
+++ b/identity/aidl/default/EicOpsImpl.cc
@@ -45,6 +45,7 @@
 
 #include "EicOps.h"
 
+using ::std::map;
 using ::std::optional;
 using ::std::string;
 using ::std::tuple;
@@ -212,7 +213,8 @@
         return false;
     }
     if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
-        eicDebug("Private key is not %zd bytes long as expected", (size_t)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;
     }
 
@@ -224,7 +226,7 @@
     }
     // ecKeyPairGetPublicKey() returns 0x04 | x | y, we don't want the leading 0x04.
     if (pubKey.value().size() != EIC_P256_PUB_KEY_SIZE + 1) {
-        eicDebug("Private key is %zd bytes long, expected %zd", pubKey.value().size(),
+        eicDebug("Public key is %zd bytes long, expected %zd", pubKey.value().size(),
                  (size_t)EIC_P256_PRIV_KEY_SIZE + 1);
         return false;
     }
@@ -272,7 +274,8 @@
         return false;
     }
     if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
-        eicDebug("Private key is not %zd bytes long as expected", (size_t)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;
     }
 
@@ -284,8 +287,8 @@
 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, uint8_t* cert,
-                     size_t* certSize) {  // inout
+                     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);
 
@@ -293,12 +296,18 @@
     pubKeyVec[0] = 0x04;
     memcpy(pubKeyVec.data() + 1, publicKey, EIC_P256_PUB_KEY_SIZE);
 
-    std::string serialDecimal = android::base::StringPrintf("%d", serial);
+    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);
+                    validityNotBefore, validityNotAfter, extensions);
     if (!certVec) {
         eicDebug("Error generating certificate");
         return false;
diff --git a/identity/aidl/default/FakeSecureHardwareProxy.cpp b/identity/aidl/default/FakeSecureHardwareProxy.cpp
index de6762f..287ffb8 100644
--- a/identity/aidl/default/FakeSecureHardwareProxy.cpp
+++ b/identity/aidl/default/FakeSecureHardwareProxy.cpp
@@ -67,6 +67,13 @@
     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) {
@@ -140,14 +147,16 @@
     return signatureOfToBeSigned;
 }
 
-// Returns encryptedCredentialKeys (80 bytes).
+// Returns encryptedCredentialKeys.
 optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishGetCredentialData(
         const string& docType) {
-    vector<uint8_t> encryptedCredentialKeys(80);
+    vector<uint8_t> encryptedCredentialKeys(116);
+    size_t size = encryptedCredentialKeys.size();
     if (!eicProvisioningFinishGetCredentialData(&ctx_, docType.c_str(),
-                                                encryptedCredentialKeys.data())) {
+                                                encryptedCredentialKeys.data(), &size)) {
         return {};
     }
+    encryptedCredentialKeys.resize(size);
     return encryptedCredentialKeys;
 }
 
@@ -162,7 +171,7 @@
     LOG(INFO) << "FakeSecureHardwarePresentationProxy created, sizeof(EicPresentation): "
               << sizeof(EicPresentation);
     return eicPresentationInit(&ctx_, testCredential, docType.c_str(),
-                               encryptedCredentialKeys.data());
+                               encryptedCredentialKeys.data(), encryptedCredentialKeys.size());
 }
 
 // Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
@@ -312,13 +321,27 @@
 }
 
 optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::deleteCredential(
-        const string& docType, size_t proofOfDeletionCborSize) {
+        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(), proofOfDeletionCborSize,
+    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
index b858dd4..6852c1a 100644
--- a/identity/aidl/default/FakeSecureHardwareProxy.h
+++ b/identity/aidl/default/FakeSecureHardwareProxy.h
@@ -32,6 +32,9 @@
 
     bool initialize(bool testCredential) override;
 
+    bool initializeForUpdate(bool testCredential, string docType,
+                             vector<uint8_t> encryptedCredentialKeys) override;
+
     bool shutdown() override;
 
     // Returns public key certificate.
@@ -122,8 +125,14 @@
     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:
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
index 270fcfa..95557b5 100644
--- a/identity/aidl/default/common/IdentityCredential.cpp
+++ b/identity/aidl/default/common/IdentityCredential.cpp
@@ -30,6 +30,7 @@
 #include <cppbor_parse.h>
 
 #include "FakeSecureHardwareProxy.h"
+#include "WritableIdentityCredential.h"
 
 namespace aidl::android::hardware::identity {
 
@@ -70,14 +71,8 @@
     docType_ = docTypeItem->value();
     testCredential_ = testCredentialItem->value();
 
-    const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
-
-    if (encryptedCredentialKeys.size() != 80) {
-        LOG(ERROR) << "Unexpected size for encrypted CredentialKeys";
-        return IIdentityCredentialStore::STATUS_INVALID_DATA;
-    }
-
-    if (!hwProxy_->initialize(testCredential_, docType_, encryptedCredentialKeys)) {
+    encryptedCredentialKeys_ = encryptedCredentialKeysItem->value();
+    if (!hwProxy_->initialize(testCredential_, docType_, encryptedCredentialKeys_)) {
         LOG(ERROR) << "hwProxy->initialize failed";
         return false;
     }
@@ -87,12 +82,32 @@
 
 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_, proofOfDeletionCbor.size());
+    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"));
@@ -111,6 +126,38 @@
     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) {
@@ -206,14 +253,17 @@
         }
     }
 
-    // Feed the auth token to secure hardware.
-    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"));
+    // 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
@@ -438,7 +488,7 @@
         }
 
         for (size_t n = 0; n < nsMap->size(); n++) {
-            auto [nsKeyItem, nsValueItem] = (*nsMap)[n];
+            auto& [nsKeyItem, nsValueItem] = (*nsMap)[n];
             const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
             const cppbor::Map* nsInnerMap = nsValueItem->asMap();
             if (nsKey == nullptr || nsInnerMap == nullptr) {
@@ -833,4 +883,19 @@
     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
index 2281821..ef9d133 100644
--- a/identity/aidl/default/common/IdentityCredential.h
+++ b/identity/aidl/default/common/IdentityCredential.h
@@ -27,7 +27,7 @@
 #include <string>
 #include <vector>
 
-#include <cppbor/cppbor.h>
+#include <cppbor.h>
 
 #include "IdentityCredentialStore.h"
 #include "SecureHardwareProxy.h"
@@ -45,9 +45,11 @@
 
 class IdentityCredential : public BnIdentityCredential {
   public:
-    IdentityCredential(sp<SecureHardwarePresentationProxy> hwProxy,
+    IdentityCredential(sp<SecureHardwareProxyFactory> hwProxyFactory,
+                       sp<SecureHardwarePresentationProxy> hwProxy,
                        const vector<uint8_t>& credentialData)
-        : hwProxy_(hwProxy),
+        : hwProxyFactory_(hwProxyFactory),
+          hwProxy_(hwProxy),
           credentialData_(credentialData),
           numStartRetrievalCalls_(0),
           expectedDeviceNameSpacesSize_(0) {}
@@ -58,6 +60,11 @@
 
     // 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;
@@ -79,8 +86,16 @@
     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_;
@@ -88,6 +103,7 @@
     // Set by initialize()
     string docType_;
     bool testCredential_;
+    vector<uint8_t> encryptedCredentialKeys_;
 
     // Set by createEphemeralKeyPair()
     vector<uint8_t> ephemeralPublicKey_;
diff --git a/identity/aidl/default/common/IdentityCredentialStore.cpp b/identity/aidl/default/common/IdentityCredentialStore.cpp
index 13f91aa..e6b5466 100644
--- a/identity/aidl/default/common/IdentityCredentialStore.cpp
+++ b/identity/aidl/default/common/IdentityCredentialStore.cpp
@@ -63,7 +63,7 @@
 
     sp<SecureHardwarePresentationProxy> hwProxy = hwProxyFactory_->createPresentationProxy();
     shared_ptr<IdentityCredential> credential =
-            ndk::SharedRefBase::make<IdentityCredential>(hwProxy, credentialData);
+            ndk::SharedRefBase::make<IdentityCredential>(hwProxyFactory_, hwProxy, credentialData);
     auto ret = credential->initialize();
     if (ret != IIdentityCredentialStore::STATUS_OK) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
diff --git a/identity/aidl/default/common/SecureHardwareProxy.h b/identity/aidl/default/common/SecureHardwareProxy.h
index b89ad87..a1ed1ef 100644
--- a/identity/aidl/default/common/SecureHardwareProxy.h
+++ b/identity/aidl/default/common/SecureHardwareProxy.h
@@ -64,6 +64,9 @@
 
     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
@@ -164,8 +167,14 @@
     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;
 };
 
diff --git a/identity/aidl/default/common/WritableIdentityCredential.cpp b/identity/aidl/default/common/WritableIdentityCredential.cpp
index 1328f36..25f129b 100644
--- a/identity/aidl/default/common/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/common/WritableIdentityCredential.cpp
@@ -23,8 +23,8 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
-#include <cppbor/cppbor.h>
-#include <cppbor/cppbor_parse.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
 
 #include <utility>
 
@@ -40,7 +40,20 @@
 
 bool WritableIdentityCredential::initialize() {
     if (!hwProxy_->initialize(testCredential_)) {
-        LOG(ERROR) << "hwProxy->initialize failed";
+        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;
diff --git a/identity/aidl/default/common/WritableIdentityCredential.h b/identity/aidl/default/common/WritableIdentityCredential.h
index c6f0628..36ad430 100644
--- a/identity/aidl/default/common/WritableIdentityCredential.h
+++ b/identity/aidl/default/common/WritableIdentityCredential.h
@@ -36,16 +36,22 @@
 
 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. Must be called
-    // right after construction.
+    // 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,
diff --git a/identity/aidl/default/identity-default.xml b/identity/aidl/default/identity-default.xml
index 37d5b81..a074250 100644
--- a/identity/aidl/default/identity-default.xml
+++ b/identity/aidl/default/identity-default.xml
@@ -1,7 +1,7 @@
 <manifest version="1.0" type="device">
     <hal format="aidl">
         <name>android.hardware.identity</name>
-        <version>2</version>
+        <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
index ec049b1..fe131eb 100644
--- a/identity/aidl/default/libeic/EicCbor.c
+++ b/identity/aidl/default/libeic/EicCbor.c
@@ -17,6 +17,7 @@
 #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;
@@ -26,6 +27,7 @@
 
 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;
@@ -33,6 +35,10 @@
     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:
@@ -53,6 +59,9 @@
             eicOpsHmacSha256Update(&cbor->digester.hmacSha256, data, size);
             break;
     }
+    if (cbor->secondaryDigesterSha256 != NULL) {
+        eicOpsSha256Update(cbor->secondaryDigesterSha256, data, size);
+    }
 
     if (cbor->size >= cbor->bufferSize) {
         cbor->size += size;
diff --git a/identity/aidl/default/libeic/EicCbor.h b/identity/aidl/default/libeic/EicCbor.h
index 4686b38..9c0f531 100644
--- a/identity/aidl/default/libeic/EicCbor.h
+++ b/identity/aidl/default/libeic/EicCbor.h
@@ -53,6 +53,9 @@
         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;
@@ -70,6 +73,14 @@
 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]);
 
diff --git a/identity/aidl/default/libeic/EicOps.h b/identity/aidl/default/libeic/EicOps.h
index da4dabf..d4fcf0e 100644
--- a/identity/aidl/default/libeic/EicOps.h
+++ b/identity/aidl/default/libeic/EicOps.h
@@ -207,14 +207,17 @@
 // 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, uint8_t* cert,
-                     size_t* certSize);  // inout
+                     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|.
diff --git a/identity/aidl/default/libeic/EicPresentation.c b/identity/aidl/default/libeic/EicPresentation.c
index d3f5556..9e033b3 100644
--- a/identity/aidl/default/libeic/EicPresentation.c
+++ b/identity/aidl/default/libeic/EicPresentation.c
@@ -19,13 +19,28 @@
 #include <inttypes.h>
 
 bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
-                         const uint8_t encryptedCredentialKeys[80]) {
-    uint8_t credentialKeys[52];
+                         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,
-                                80,
+                                encryptedCredentialKeysSize,
                                 // DocType is the additionalAuthenticatedData
                                 (const uint8_t*)docType, eicStrLen(docType), credentialKeys)) {
         eicDebug("Error decrypting CredentialKeys");
@@ -34,25 +49,42 @@
 
     // 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
+    //              bstr,   ; credentialPrivKey, the private key for credentialKey
     //         ]
     //
-    // where storageKey is 16 bytes and credentialPrivateKey is 32 bytes.
+    // Feature version 202101:
     //
-    // So the first two bytes will be 0x82 0x50 indicating resp. an array of two elements
-    // and a bstr of 16 elements. Sixteen bytes later (offset 18 and 19) there will be
-    // a bstr of 32 bytes. It's encoded as two bytes 0x58 and 0x20.
+    //         CredentialKeys = [
+    //              bstr,   ; storageKey, a 128-bit AES key
+    //              bstr,   ; credentialPrivKey, the private key for credentialKey
+    //              bstr    ; proofOfProvisioning SHA-256
+    //         ]
     //
-    if (credentialKeys[0] != 0x82 || credentialKeys[1] != 0x50 || credentialKeys[18] != 0x58 ||
-        credentialKeys[19] != 0x20) {
+    // 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;
 }
 
@@ -61,6 +93,35 @@
                                            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");
@@ -73,7 +134,8 @@
     if (!eicOpsSignEcKey(signingKeyPub, ctx->credentialPrivateKey, 1,
                          "Android Identity Credential Key",                 // issuer CN
                          "Android Identity Credential Authentication Key",  // subject CN
-                         validityNotBefore, validityNotAfter, publicKeyCert, publicKeyCertSize)) {
+                         validityNotBefore, validityNotAfter, proofOfBinding, proofOfBindingSize,
+                         publicKeyCert, publicKeyCertSize)) {
         eicDebug("Error creating certificate for signing key");
         return false;
     }
@@ -274,6 +336,18 @@
                                  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,
@@ -298,18 +372,9 @@
         return false;
     }
 
+    // Only ACP with auth-on-every-presentation - those with timeout == 0 - need the
+    // challenge to match...
     if (timeoutMillis == 0) {
-        if (ctx->authTokenChallenge == 0) {
-            eicDebug("No challenge in authToken");
-            return false;
-        }
-
-        // If we didn't create a challenge, too bad but user auth with
-        // timeoutMillis set to 0 needs it.
-        if (ctx->authChallenge == 0) {
-            eicDebug("No challenge was created for this session");
-            return false;
-        }
         if (ctx->authTokenChallenge != ctx->authChallenge) {
             eicDebug("Challenge in authToken (%" PRIu64
                      ") doesn't match the challenge "
@@ -674,7 +739,8 @@
 }
 
 bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
-                                     size_t proofOfDeletionCborSize,
+                                     const uint8_t* challenge, size_t challengeSize,
+                                     bool includeChallenge, size_t proofOfDeletionCborSize,
                                      uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
     EicCbor cbor;
 
@@ -712,9 +778,12 @@
     eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, proofOfDeletionCborSize);
 
     // Finally, the CBOR that we're actually signing.
-    eicCborAppendArray(&cbor, 3);
+    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];
@@ -726,3 +795,59 @@
 
     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
index d798962..7cad068 100644
--- a/identity/aidl/default/libeic/EicPresentation.h
+++ b/identity/aidl/default/libeic/EicPresentation.h
@@ -31,6 +31,8 @@
 #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];
 
@@ -79,12 +81,17 @@
     // 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[80]);
+                         const uint8_t* encryptedCredentialKeys,
+                         size_t encryptedCredentialKeysSize);
 
 bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType, time_t now,
                                            uint8_t* publicKeyCert, size_t* publicKeyCertSize,
@@ -219,9 +226,19 @@
 // where content is set to the ProofOfDeletion CBOR.
 //
 bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
-                                     size_t proofOfDeletionCborSize,
+                                     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
diff --git a/identity/aidl/default/libeic/EicProvisioning.c b/identity/aidl/default/libeic/EicProvisioning.c
index f16605c..3b4148e 100644
--- a/identity/aidl/default/libeic/EicProvisioning.c
+++ b/identity/aidl/default/libeic/EicProvisioning.c
@@ -26,10 +26,84 @@
     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)) {
@@ -96,6 +170,9 @@
     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);
@@ -260,14 +337,23 @@
 }
 
 bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
-                                            uint8_t encryptedCredentialKeys[80]) {
+                                            uint8_t* encryptedCredentialKeys,
+                                            size_t* encryptedCredentialKeysSize) {
     EicCbor cbor;
-    uint8_t cborBuf[52];
+    uint8_t cborBuf[86];
+
+    if (*encryptedCredentialKeysSize < 86 + 28) {
+        eicDebug("encryptedCredentialKeysSize is %zd which is insufficient");
+        return false;
+    }
 
     eicCborInit(&cbor, cborBuf, sizeof(cborBuf));
-    eicCborAppendArray(&cbor, 2);
+    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;
@@ -285,6 +371,7 @@
         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
index 836d16e..f064787 100644
--- a/identity/aidl/default/libeic/EicProvisioning.h
+++ b/identity/aidl/default/libeic/EicProvisioning.h
@@ -31,7 +31,7 @@
 #define EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS 32
 
 typedef struct {
-    // Set by eicCreateCredentialKey.
+    // Set by eicCreateCredentialKey() OR eicProvisioningInitForUpdate()
     uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
 
     int numEntryCounts;
@@ -43,6 +43,7 @@
     size_t curEntrySize;
     size_t curEntryNumBytesReceived;
 
+    // Set by eicProvisioningInit() OR eicProvisioningInitForUpdate()
     uint8_t storageKey[EIC_AES_128_KEY_SIZE];
 
     size_t expectedCborSizeAtEnd;
@@ -50,13 +51,23 @@
     // 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,
@@ -107,14 +118,18 @@
 //   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 52 bytes and consequently
-// |encryptedCredentialKeys| will be 52 + 28 = 80 bytes.
+// 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[80]);
+                                            uint8_t* encryptedCredentialKeys,
+                                            size_t* encryptedCredentialKeysSize);
 
 #ifdef __cplusplus
 }
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..d3addf4
--- /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({0}, "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/VtsHalIdentityEndToEndTest.cpp b/identity/aidl/vts/EndToEndTests.cpp
similarity index 89%
rename from identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
rename to identity/aidl/vts/EndToEndTests.cpp
index cdecb97..67db915 100644
--- a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
+++ b/identity/aidl/vts/EndToEndTests.cpp
@@ -29,7 +29,7 @@
 #include <map>
 #include <tuple>
 
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
 
 namespace android::hardware::identity {
 
@@ -50,18 +50,20 @@
 
 using test_utils::validateAttestationCertificate;
 
-class IdentityAidl : public testing::TestWithParam<std::string> {
+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(IdentityAidl, hardwareInformation) {
+TEST_P(EndToEndTests, hardwareInformation) {
     HardwareInformation info;
     ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
     ASSERT_GT(info.credentialStoreName.size(), 0);
@@ -69,20 +71,21 @@
     ASSERT_GE(info.dataChunkSize, 256);
 }
 
-tuple<bool, string, vector<uint8_t>, vector<uint8_t>> extractFromTestCredentialData(
-        const vector<uint8_t>& credentialData) {
+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);
+        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);
+        return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
     }
 
     const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
@@ -92,7 +95,7 @@
     const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
     if (docTypeItem == nullptr || testCredentialItem == nullptr ||
         encryptedCredentialKeysItem == nullptr) {
-        return make_tuple(false, docType, storageKey, credentialPrivKey);
+        return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
     }
 
     docType = docTypeItem->value();
@@ -103,28 +106,38 @@
     optional<vector<uint8_t>> decryptedCredentialKeys =
             support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
     if (!decryptedCredentialKeys) {
-        return make_tuple(false, docType, storageKey, credentialPrivKey);
+        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);
+        return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
     }
     const cppbor::Array* dckArrayItem = dckItem->asArray();
-    if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
-        return make_tuple(false, docType, storageKey, credentialPrivKey);
+    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);
+        return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
     }
     storageKey = storageKeyItem->value();
     credentialPrivKey = credentialPrivKeyItem->value();
-    return make_tuple(true, docType, storageKey, credentialPrivKey);
+    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(IdentityAidl, createAndRetrieveCredential) {
+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;
@@ -218,7 +231,7 @@
     optional<vector<uint8_t>> proofOfProvisioning =
             support::coseSignGetPayload(proofOfProvisioningSignature);
     ASSERT_TRUE(proofOfProvisioning);
-    cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
+    cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
     EXPECT_EQ(
             "[\n"
             "  'ProofOfProvisioning',\n"
@@ -277,8 +290,9 @@
 
     // 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] =
+    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
@@ -291,6 +305,13 @@
     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;
@@ -318,8 +339,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> sessionTranscriptEncoded = sessionTranscript.encode();
 
     vector<uint8_t> itemsRequestBytes =
@@ -332,7 +353,7 @@
                                                              .add("Home address", true))
                                 .add("Image", cppbor::Map().add("Portrait image", false)))
                     .encode();
-    cborPretty = support::cborPrettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
+    cborPretty = cppbor::prettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
     EXPECT_EQ(
             "{\n"
             "  'nameSpaces' : {\n"
@@ -352,10 +373,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(readerKey, {},                     // content
                                    encodedReaderAuthenticationBytes,  // detached content
@@ -422,7 +443,7 @@
     vector<uint8_t> mac;
     vector<uint8_t> deviceNameSpacesEncoded;
     ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
-    cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
+    cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
     ASSERT_EQ(
             "{\n"
             "  'PersonalData' : {\n"
@@ -441,10 +462,11 @@
     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>> 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
@@ -465,7 +487,7 @@
                                 testEntriesEntryCounts)
                         .isOk());
     ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
-    cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
+    cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
     ASSERT_EQ("{}", cborPretty);
     // Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
     calculatedMac = support::calcMac(sessionTranscript.encode(),  // SessionTranscript
@@ -487,7 +509,7 @@
                                 testEntriesEntryCounts)
                         .isOk());
     ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
-    cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
+    cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
     ASSERT_EQ("{}", cborPretty);
     // Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
     calculatedMac = support::calcMac(sessionTranscript.encode(),  // SessionTranscript
@@ -498,13 +520,11 @@
     EXPECT_EQ(mac, calculatedMac);
 }
 
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(IdentityAidl);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EndToEndTests);
 INSTANTIATE_TEST_SUITE_P(
-        Identity, IdentityAidl,
+        Identity, EndToEndTests,
         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
 
diff --git a/identity/aidl/vts/ProveOwnershipTests.cpp b/identity/aidl/vts/ProveOwnershipTests.cpp
new file mode 100644
index 0000000..fa0e293
--- /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({0}, "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 0a9fdc0..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 =
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 327493c..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();
     }
 
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.cpp b/identity/aidl/vts/Util.cpp
similarity index 98%
rename from identity/aidl/vts/VtsIdentityTestUtils.cpp
rename to identity/aidl/vts/Util.cpp
index 3b10651..1148cb0 100644
--- a/identity/aidl/vts/VtsIdentityTestUtils.cpp
+++ b/identity/aidl/vts/Util.cpp
@@ -14,15 +14,18 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "VtsIdentityTestUtils"
+#define LOG_TAG "Util"
 
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
+
+#include <android-base/logging.h>
 
 #include <aidl/Gtest.h>
-#include <android-base/logging.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 {
@@ -35,6 +38,7 @@
 
 using ::android::sp;
 using ::android::String16;
+using ::android::base::StringPrintf;
 using ::android::binder::Status;
 using ::keymaster::X509_Ptr;
 
@@ -86,7 +90,7 @@
 
     return support::ecPublicKeyGenerateCertificate(readerPublicKey.value(), readerKey.value(),
                                                    serialDecimal, issuer, subject,
-                                                   validityNotBefore, validityNotAfter);
+                                                   validityNotBefore, validityNotAfter, {});
 }
 
 optional<vector<SecureAccessControlProfile>> addAccessControlProfiles(
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.h b/identity/aidl/vts/Util.h
similarity index 99%
rename from identity/aidl/vts/VtsIdentityTestUtils.h
rename to identity/aidl/vts/Util.h
index 85c24f8..80e52a2 100644
--- a/identity/aidl/vts/VtsIdentityTestUtils.h
+++ b/identity/aidl/vts/Util.h
@@ -21,6 +21,7 @@
 #include <android/hardware/identity/support/IdentityCredentialSupport.h>
 #include <cppbor.h>
 #include <cppbor_parse.h>
+#include <gtest/gtest.h>
 
 namespace android::hardware::identity::test_utils {
 
diff --git a/identity/aidl/vts/VtsAttestationTests.cpp b/identity/aidl/vts/VtsAttestationTests.cpp
index 5529853..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 {
 
diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
index 1629a0c..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 {
 
@@ -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"
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 093120d..4547624 100644
--- a/identity/support/src/IdentityCredentialSupport.cpp
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -148,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) {
@@ -534,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) {
@@ -935,9 +640,16 @@
 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) {
+    // 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::KEYMASTER_4_1,
+                                                  KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT);
+
     keymaster_error_t error;
     ::keymaster::CertificateChain attestation_chain =
-            ::keymaster::getAttestationChain(KM_ALGORITHM_EC, &error);
+            context.GetAttestationChain(KM_ALGORITHM_EC, &error);
     if (KM_ERROR_OK != error) {
         LOG(ERROR) << "Error getting attestation chain " << error;
         return {};
@@ -957,12 +669,6 @@
         }
         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";
-        return {};
-    }
 
     ::keymaster::X509_NAME_Ptr subjectName;
     if (KM_ERROR_OK !=
@@ -976,8 +682,11 @@
 
     i2d_X509_NAME(subjectName.get(), &subjectPtr);
 
+    uint64_t nowMilliSeconds = time(nullptr) * 1000;
     ::keymaster::AuthorizationSet auth_set(
             ::keymaster::AuthorizationSetBuilder()
+                    .Authorization(::keymaster::TAG_CERTIFICATE_NOT_BEFORE, nowMilliSeconds)
+                    .Authorization(::keymaster::TAG_CERTIFICATE_NOT_AFTER, expireTimeMilliSeconds)
                     .Authorization(::keymaster::TAG_ATTESTATION_CHALLENGE, challenge.data(),
                                    challenge.size())
                     .Authorization(::keymaster::TAG_ACTIVE_DATETIME, activeTimeMilliSeconds)
@@ -1016,25 +725,17 @@
     }
     ::keymaster::AuthorizationSet hwEnforced(hwEnforcedBuilder);
 
-    // 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::KEYMASTER_4_1,
-                                                  KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT);
-
-    ::keymaster::CertificateChain cert_chain_out = generate_attestation_from_EVP(
-            key, swEnforced, hwEnforced, auth_set, context, move(attestation_chain),
-            *attestation_signing_key, &error);
+    ::keymaster::CertificateChain cert_chain_out = generate_attestation(
+            key, swEnforced, hwEnforced, auth_set, {} /* attest_key */, context, &error);
 
     if (KM_ERROR_OK != error) {
-        LOG(ERROR) << "Error generate attestation from EVP key" << error;
+        LOG(ERROR) << "Error generating attestation from EVP key: " << error;
         return {};
     }
 
     // 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>(
@@ -1221,8 +922,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;
 }
 
@@ -1379,7 +1091,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) !=
@@ -1482,6 +1195,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 {};
@@ -1650,6 +1389,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)) {
@@ -2170,7 +1947,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";
@@ -2213,7 +1990,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";
@@ -2365,9 +2142,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
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 b6e54ca..37e522d 100644
--- a/input/classifier/1.0/Android.bp
+++ b/input/classifier/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.input.classifier@1.0",
     root: "android.hardware",
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 99fdb8c..5964a7e 100644
--- a/input/classifier/1.0/vts/functional/Android.bp
+++ b/input/classifier/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: "VtsHalInputClassifierV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/input/common/1.0/Android.bp b/input/common/1.0/Android.bp
index 07ced7a..ed0ab98 100644
--- a/input/common/1.0/Android.bp
+++ b/input/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.input.common@1.0",
     root: "android.hardware",
diff --git a/ir/1.0/Android.bp b/ir/1.0/Android.bp
index 6a521f7..7ed7429 100644
--- a/ir/1.0/Android.bp
+++ b/ir/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.ir@1.0",
     root: "android.hardware",
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/keymaster/3.0/Android.bp b/keymaster/3.0/Android.bp
index d0c7a7c..12f6e3c 100644
--- a/keymaster/3.0/Android.bp
+++ b/keymaster/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.keymaster@3.0",
     root: "android.hardware",
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/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/4.0/Android.bp b/keymaster/4.0/Android.bp
index 5774718..8b4bc5b 100644
--- a/keymaster/4.0/Android.bp
+++ b/keymaster/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.keymaster@4.0",
     root: "android.hardware",
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/include/keymasterV4_0/keymaster_tags.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
index 8d6e74a..ea40971 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
@@ -117,6 +117,9 @@
 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);
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/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
index f196928..e0d60fc 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -136,6 +136,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"
@@ -1905,21 +1953,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 +2567,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());
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 4e7e944..e76afbd 100644
--- a/keymaster/4.1/Android.bp
+++ b/keymaster/4.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.keymaster@4.1",
     root: "android.hardware",
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/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/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..e46cb48 100644
--- a/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp
+++ b/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -221,71 +221,78 @@
 
 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]));
     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]));
     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 */
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/VerificationToken.aidl b/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
index f053254..5efd937 100644
--- a/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
@@ -43,7 +43,7 @@
     /**
      * 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 ae6d37c..95eb5ae 100644
--- a/light/2.0/Android.bp
+++ b/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.light@2.0",
     root: "android.hardware",
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/aidl/Android.bp b/light/aidl/Android.bp
index fbcdb32..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,
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/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 5dbbadd..175b8a5 100644
--- a/media/bufferpool/1.0/Android.bp
+++ b/media/bufferpool/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.bufferpool@1.0",
     root: "android.hardware",
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 089ce98..dc56fef 100644
--- a/media/c2/1.0/Android.bp
+++ b/media/c2/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.c2@1.0",
     root: "android.hardware",
diff --git a/media/c2/1.1/Android.bp b/media/c2/1.1/Android.bp
index a3d31df..885a4c8 100644
--- a/media/c2/1.1/Android.bp
+++ b/media/c2/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.media.c2@1.1",
     root: "android.hardware",
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/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/common/Android.bp b/media/omx/1.0/vts/functional/common/Android.bp
index 89df4ff..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"],
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 9184c56..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;
 }
 
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 b16c772..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
@@ -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/store/Android.bp b/media/omx/1.0/vts/functional/store/Android.bp
index 28d12ff..b34fff1 100644
--- a/media/omx/1.0/vts/functional/store/Android.bp
+++ b/media/omx/1.0/vts/functional/store/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_0TargetStoreTest",
     defaults: ["VtsHalMediaOmxV1_0Defaults"],
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 67b9895..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,9 +1415,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));
 }
 
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VideoDecHidlTest);
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 3c0734e..f24c6d1 100644
--- a/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoEncTest.cpp
+++ b/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoEncTest.cpp
@@ -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,9 +1450,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);
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/aidl/Android.bp b/memtrack/aidl/Android.bp
index fe4d01b..29fec24 100644
--- a/memtrack/aidl/Android.bp
+++ b/memtrack/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.memtrack",
     vendor_available: 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
index 00abff9..3e25c56 100644
--- 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
@@ -1,14 +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.
+ *////////////////////////////////////////////////////////////////////////////////
 // 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/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
index 844a1bb..2e2b68e 100644
--- 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
@@ -1,14 +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.
+ *////////////////////////////////////////////////////////////////////////////////
 // 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/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
index 09ecefc..0e15ce3 100644
--- 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
@@ -1,14 +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.
+ *////////////////////////////////////////////////////////////////////////////////
 // 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/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
index 7f3f939..b19869e 100644
--- 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
@@ -1,14 +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.
+ *////////////////////////////////////////////////////////////////////////////////
 // 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,5 +38,4 @@
   GRAPHICS = 2,
   MULTIMEDIA = 3,
   CAMERA = 4,
-  NUM_TYPES = 5,
 }
diff --git a/memtrack/aidl/android/hardware/memtrack/IMemtrack.aidl b/memtrack/aidl/android/hardware/memtrack/IMemtrack.aidl
index 18587ee..e78d4d7 100644
--- a/memtrack/aidl/android/hardware/memtrack/IMemtrack.aidl
+++ b/memtrack/aidl/android/hardware/memtrack/IMemtrack.aidl
@@ -30,6 +30,22 @@
  * 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
+ * `dumpsys meminfo` and should be reported as described below:
+ *
+ * - MemtrackType::GRAPHICS and MemtrackRecord::FLAG_SMAPS_UNACCOUNTED
+ *     This should report the PSS of all DMA buffers mapped by the process
+ *     with the specified PID. This PSS can be calculated using ReadDmaBufPss()
+ *     form libdmabufinfo.
+ *
+ * - 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.
+ *
  * Constructor for the interface should be used to perform memtrack management
  * setup actions and must be called once before any calls to getMemory().
  */
@@ -76,7 +92,10 @@
      * 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();
+    DeviceInfo[] getGpuDeviceInfo();
 }
diff --git a/memtrack/aidl/android/hardware/memtrack/MemtrackType.aidl b/memtrack/aidl/android/hardware/memtrack/MemtrackType.aidl
index 715c6bf..5db735a 100644
--- a/memtrack/aidl/android/hardware/memtrack/MemtrackType.aidl
+++ b/memtrack/aidl/android/hardware/memtrack/MemtrackType.aidl
@@ -27,5 +27,4 @@
     GRAPHICS = 2,
     MULTIMEDIA = 3,
     CAMERA = 4,
-    NUM_TYPES,
 }
diff --git a/memtrack/aidl/default/Android.bp b/memtrack/aidl/default/Android.bp
index 52f88c8..7a7feea 100644
--- a/memtrack/aidl/default/Android.bp
+++ b/memtrack/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.memtrack-service.example",
     relative_install_path: "hw",
@@ -21,7 +30,7 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.memtrack-ndk_platform",
+        "android.hardware.memtrack-V1-ndk_platform",
     ],
     srcs: [
         "main.cpp",
diff --git a/memtrack/aidl/default/Memtrack.cpp b/memtrack/aidl/default/Memtrack.cpp
index 7361719..49a6582 100644
--- a/memtrack/aidl/default/Memtrack.cpp
+++ b/memtrack/aidl/default/Memtrack.cpp
@@ -26,7 +26,8 @@
     if (pid < 0) {
         return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
     }
-    if (type < MemtrackType::OTHER || type >= MemtrackType::NUM_TYPES) {
+    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();
@@ -35,6 +36,8 @@
 
 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();
 }
 
diff --git a/memtrack/aidl/vts/Android.bp b/memtrack/aidl/vts/Android.bp
index ea36677..8614b47 100644
--- a/memtrack/aidl/vts/Android.bp
+++ b/memtrack/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: "VtsHalMemtrackTargetTest",
     defaults: [
@@ -7,11 +16,12 @@
     srcs: ["VtsHalMemtrackTargetTest.cpp"],
     shared_libs: [
         "libbinder_ndk",
+        "libvintf",
     ],
     static_libs: [
-        "android.hardware.memtrack-unstable-ndk_platform",
+        "android.hardware.memtrack-V1-ndk_platform",
     ],
     test_suites: [
-        "vts-core",
+        "vts",
     ],
 }
diff --git a/memtrack/aidl/vts/VtsHalMemtrackTargetTest.cpp b/memtrack/aidl/vts/VtsHalMemtrackTargetTest.cpp
index 4d33101..8905f50 100644
--- a/memtrack/aidl/vts/VtsHalMemtrackTargetTest.cpp
+++ b/memtrack/aidl/vts/VtsHalMemtrackTargetTest.cpp
@@ -21,11 +21,15 @@
 #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:
@@ -42,17 +46,19 @@
 
 TEST_P(MemtrackAidlTest, GetMemoryInvalidPid) {
     int pid = -1;
-    MemtrackType type = MemtrackType::OTHER;
-    std::vector<MemtrackRecord> records;
 
-    auto status = memtrack_->getMemory(pid, type, &records);
+    for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+        std::vector<MemtrackRecord> records;
 
-    EXPECT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+        auto status = memtrack_->getMemory(pid, type, &records);
+
+        EXPECT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+    }
 }
 
 TEST_P(MemtrackAidlTest, GetMemoryInvalidType) {
     int pid = 1;
-    MemtrackType type = MemtrackType::NUM_TYPES;
+    MemtrackType type = static_cast<MemtrackType>(-1);
     std::vector<MemtrackRecord> records;
 
     auto status = memtrack_->getMemory(pid, type, &records);
@@ -62,12 +68,13 @@
 
 TEST_P(MemtrackAidlTest, GetMemory) {
     int pid = 1;
-    MemtrackType type = MemtrackType::OTHER;
-    std::vector<MemtrackRecord> records;
+    for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+        std::vector<MemtrackRecord> records;
 
-    auto status = memtrack_->getMemory(pid, type, &records);
+        auto status = memtrack_->getMemory(pid, type, &records);
 
-    EXPECT_TRUE(status.isOk());
+        EXPECT_TRUE(status.isOk());
+    }
 }
 
 TEST_P(MemtrackAidlTest, GetGpuDeviceInfo) {
@@ -75,7 +82,23 @@
 
     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);
diff --git a/neuralnetworks/1.0/Android.bp b/neuralnetworks/1.0/Android.bp
index 20de9d5..7bc65ff 100644
--- a/neuralnetworks/1.0/Android.bp
+++ b/neuralnetworks/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.neuralnetworks@1.0",
     root: "android.hardware",
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
index d033617..0ad9926 100644
--- a/neuralnetworks/1.0/utils/Android.bp
+++ b/neuralnetworks/1.0/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: "neuralnetworks_utils_hal_1_0",
     defaults: ["neuralnetworks_utils_defaults"],
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
index f2cbe93..7849ca7 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h
@@ -41,10 +41,12 @@
 
     Burst(PrivateConstructorTag tag, nn::SharedPreparedModel preparedModel);
 
-    OptionalCacheHold cacheMemory(const nn::Memory& memory) const override;
+    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 override;
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
 
   private:
     const nn::SharedPreparedModel kPreparedModel;
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
index d3d933b..5d4bdbc 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
@@ -36,7 +36,7 @@
 GeneralResult<Operation> unvalidatedConvert(const hal::V1_0::Operation& operation);
 GeneralResult<Model::OperandValues> unvalidatedConvert(
         const hardware::hidl_vec<uint8_t>& operandValues);
-GeneralResult<Memory> unvalidatedConvert(const hardware::hidl_memory& memory);
+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);
@@ -65,7 +65,7 @@
 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::Memory& memory);
+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);
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
index 4681b9e..db3b2ad 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
@@ -52,7 +52,6 @@
     const std::string& getVersionString() const override;
     nn::Version getFeatureLevel() const override;
     nn::DeviceType getType() const override;
-    bool isUpdatable() 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;
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
index 4cec545..1baabdf 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
@@ -22,10 +22,15 @@
 #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);
@@ -44,6 +49,21 @@
     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/Burst.cpp b/neuralnetworks/1.0/utils/src/Burst.cpp
index 384bd9b..e3a9757 100644
--- a/neuralnetworks/1.0/utils/src/Burst.cpp
+++ b/neuralnetworks/1.0/utils/src/Burst.cpp
@@ -20,6 +20,7 @@
 #include <nnapi/IBurst.h>
 #include <nnapi/IPreparedModel.h>
 #include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
 
 #include <memory>
@@ -43,13 +44,15 @@
     CHECK(kPreparedModel != nullptr);
 }
 
-Burst::OptionalCacheHold Burst::cacheMemory(const nn::Memory& /*memory*/) const {
+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 {
-    return kPreparedModel->execute(request, measure, {}, {});
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalTimePoint& deadline,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration);
 }
 
 }  // 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
index fde7346..c0498eb 100644
--- a/neuralnetworks/1.0/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.0/utils/src/Conversions.cpp
@@ -35,6 +35,8 @@
 #include <utility>
 #include <variant>
 
+#include "Utils.h"
+
 namespace {
 
 template <typename Type>
@@ -42,8 +44,6 @@
     return static_cast<std::underlying_type_t<Type>>(value);
 }
 
-constexpr auto kVersion = android::nn::Version::ANDROID_OC_MR1;
-
 }  // namespace
 
 namespace android::nn {
@@ -53,13 +53,13 @@
 using hardware::hidl_vec;
 
 template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
         std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
         const hidl_vec<Type>& arguments) {
-    std::vector<unvalidatedConvertOutput<Type>> canonical;
+    std::vector<UnvalidatedConvertOutput<Type>> canonical;
     canonical.reserve(arguments.size());
     for (const auto& argument : arguments) {
         canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
@@ -68,16 +68,9 @@
 }
 
 template <typename Type>
-decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
     auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
-    const auto maybeVersion = validate(canonical);
-    if (!maybeVersion.has_value()) {
-        return error() << maybeVersion.error();
-    }
-    const auto version = maybeVersion.value();
-    if (version > kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
-    }
+    NN_TRY(hal::V1_0::utils::compliantVersion(canonical));
     return canonical;
 }
 
@@ -153,8 +146,8 @@
     return Model::OperandValues(operandValues.data(), operandValues.size());
 }
 
-GeneralResult<Memory> unvalidatedConvert(const hidl_memory& memory) {
-    return createSharedMemoryFromHidlMemory(memory);
+GeneralResult<SharedMemory> unvalidatedConvert(const hidl_memory& memory) {
+    return hal::utils::createSharedMemoryFromHidlMemory(memory);
 }
 
 GeneralResult<Model> unvalidatedConvert(const hal::V1_0::Model& model) {
@@ -162,7 +155,7 @@
 
     // Verify number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(model.operands.size(), operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(model.operands.size(), operations));
     CHECK(model.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < model.operands.size(); ++i) {
         if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -248,13 +241,13 @@
 namespace {
 
 template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
         std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
         const std::vector<Type>& arguments) {
-    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+    hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
     for (size_t i = 0; i < arguments.size(); ++i) {
         halObject[i] = NN_TRY(utils::unvalidatedConvert(arguments[i]));
     }
@@ -262,15 +255,8 @@
 }
 
 template <typename Type>
-decltype(utils::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
-    const auto maybeVersion = nn::validate(canonical);
-    if (!maybeVersion.has_value()) {
-        return nn::error() << maybeVersion.error();
-    }
-    const auto version = maybeVersion.value();
-    if (version > kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
-    }
+nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
+    NN_TRY(compliantVersion(canonical));
     return utils::unvalidatedConvert(canonical);
 }
 
@@ -346,9 +332,8 @@
     return hidl_vec<uint8_t>(operandValues.data(), operandValues.data() + operandValues.size());
 }
 
-nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
-    return hidl_memory(memory.name, NN_TRY(hal::utils::hidlHandleFromSharedHandle(memory.handle)),
-                       memory.size);
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
+    return hal::utils::createHidlMemoryFromSharedMemory(memory);
 }
 
 nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
@@ -361,7 +346,7 @@
 
     // Update number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(operands.size(), model.main.operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), model.main.operations));
     CHECK(operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < operands.size(); ++i) {
         operands[i].numberOfConsumers = numberOfConsumers[i];
@@ -392,7 +377,7 @@
 }
 
 nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Request::MemoryPool& memoryPool) {
-    return unvalidatedConvert(std::get<nn::Memory>(memoryPool));
+    return unvalidatedConvert(std::get<nn::SharedMemory>(memoryPool));
 }
 
 nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request) {
diff --git a/neuralnetworks/1.0/utils/src/Device.cpp b/neuralnetworks/1.0/utils/src/Device.cpp
index bb31a26..93bd81a 100644
--- a/neuralnetworks/1.0/utils/src/Device.cpp
+++ b/neuralnetworks/1.0/utils/src/Device.cpp
@@ -106,10 +106,6 @@
     return nn::DeviceType::OTHER;
 }
 
-bool Device::isUpdatable() const {
-    return false;
-}
-
 const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
     return kExtensions;
 }
diff --git a/neuralnetworks/1.0/utils/test/MockDevice.h b/neuralnetworks/1.0/utils/test/MockDevice.h
index 0fb59e3..7c399ec 100644
--- a/neuralnetworks/1.0/utils/test/MockDevice.h
+++ b/neuralnetworks/1.0/utils/test/MockDevice.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE
+#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>
@@ -83,4 +83,4 @@
 
 }  // namespace android::hardware::neuralnetworks::V1_0::utils
 
-#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE
+#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
index 7a48a83..03f1a4b 100644
--- a/neuralnetworks/1.0/utils/test/MockPreparedModel.h
+++ b/neuralnetworks/1.0/utils/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL
+#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>
@@ -82,4 +82,4 @@
 
 }  // namespace android::hardware::neuralnetworks::V1_0::utils
 
-#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL
+#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
index a5cbc72..f19ed77 100644
--- a/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
@@ -224,7 +224,19 @@
     EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
 }
 
-// TODO: test burst execution if/when it is added to nn::IPreparedModel.
+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
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.1/Android.bp b/neuralnetworks/1.1/Android.bp
index 52d866f..772e5e6 100644
--- a/neuralnetworks/1.1/Android.bp
+++ b/neuralnetworks/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.neuralnetworks@1.1",
     root: "android.hardware",
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
index fe0c80a..d9e82d4 100644
--- a/neuralnetworks/1.1/utils/Android.bp
+++ b/neuralnetworks/1.1/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: "neuralnetworks_utils_hal_1_1",
     defaults: ["neuralnetworks_utils_defaults"],
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
index 3aec8ee..5e224b5 100644
--- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
@@ -52,7 +52,6 @@
     const std::string& getVersionString() const override;
     nn::Version getFeatureLevel() const override;
     nn::DeviceType getType() const override;
-    bool isUpdatable() 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;
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
index 052d88e..a8cf8cf 100644
--- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
@@ -22,12 +22,16 @@
 #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) {
@@ -47,6 +51,21 @@
     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/Conversions.cpp b/neuralnetworks/1.1/utils/src/Conversions.cpp
index b47f25a..467ceb3 100644
--- a/neuralnetworks/1.1/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.1/utils/src/Conversions.cpp
@@ -35,11 +35,7 @@
 #include <type_traits>
 #include <utility>
 
-namespace {
-
-constexpr auto kVersion = android::nn::Version::ANDROID_P;
-
-}  // namespace
+#include "Utils.h"
 
 namespace android::nn {
 namespace {
@@ -47,13 +43,13 @@
 using hardware::hidl_vec;
 
 template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
         std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
         const hidl_vec<Type>& arguments) {
-    std::vector<unvalidatedConvertOutput<Type>> canonical;
+    std::vector<UnvalidatedConvertOutput<Type>> canonical;
     canonical.reserve(arguments.size());
     for (const auto& argument : arguments) {
         canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
@@ -62,16 +58,9 @@
 }
 
 template <typename Type>
-decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
     auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
-    const auto maybeVersion = validate(canonical);
-    if (!maybeVersion.has_value()) {
-        return error() << maybeVersion.error();
-    }
-    const auto version = maybeVersion.value();
-    if (version > kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
-    }
+    NN_TRY(hal::V1_1::utils::compliantVersion(canonical));
     return canonical;
 }
 
@@ -111,7 +100,7 @@
 
     // Verify number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(model.operands.size(), operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(model.operands.size(), operations));
     CHECK(model.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < model.operands.size(); ++i) {
         if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -175,18 +164,18 @@
     return V1_0::utils::unvalidatedConvert(operandValues);
 }
 
-nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
     return V1_0::utils::unvalidatedConvert(memory);
 }
 
 template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
         std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
         const std::vector<Type>& arguments) {
-    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+    hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
     for (size_t i = 0; i < arguments.size(); ++i) {
         halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
     }
@@ -194,16 +183,9 @@
 }
 
 template <typename Type>
-decltype(utils::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
-    const auto maybeVersion = nn::validate(canonical);
-    if (!maybeVersion.has_value()) {
-        return nn::error() << maybeVersion.error();
-    }
-    const auto version = maybeVersion.value();
-    if (version > kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
-    }
-    return utils::unvalidatedConvert(canonical);
+nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
+    NN_TRY(compliantVersion(canonical));
+    return unvalidatedConvert(canonical);
 }
 
 }  // anonymous namespace
@@ -241,7 +223,7 @@
 
     // Update number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(operands.size(), model.main.operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), model.main.operations));
     CHECK(operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < operands.size(); ++i) {
         operands[i].numberOfConsumers = numberOfConsumers[i];
diff --git a/neuralnetworks/1.1/utils/src/Device.cpp b/neuralnetworks/1.1/utils/src/Device.cpp
index d2ef57f..3197ef4 100644
--- a/neuralnetworks/1.1/utils/src/Device.cpp
+++ b/neuralnetworks/1.1/utils/src/Device.cpp
@@ -106,10 +106,6 @@
     return nn::DeviceType::UNKNOWN;
 }
 
-bool Device::isUpdatable() const {
-    return false;
-}
-
 const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
     return kExtensions;
 }
diff --git a/neuralnetworks/1.1/utils/test/MockDevice.h b/neuralnetworks/1.1/utils/test/MockDevice.h
index 3b92e58..db7392d 100644
--- a/neuralnetworks/1.1/utils/test/MockDevice.h
+++ b/neuralnetworks/1.1/utils/test/MockDevice.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE
+#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>
@@ -92,4 +92,4 @@
 
 }  // namespace android::hardware::neuralnetworks::V1_1::utils
 
-#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE
+#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
index aba731e..257397d 100644
--- a/neuralnetworks/1.1/utils/test/MockPreparedModel.h
+++ b/neuralnetworks/1.1/utils/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL
+#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>
@@ -41,4 +41,4 @@
 
 }  // namespace android::hardware::neuralnetworks::V1_0::utils
 
-#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL
+#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.2/Android.bp b/neuralnetworks/1.2/Android.bp
index 9e1db1e..2b83d39 100644
--- a/neuralnetworks/1.2/Android.bp
+++ b/neuralnetworks/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.neuralnetworks@1.2",
     root: "android.hardware",
diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal
index 7441a54..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,
 };
 
 /**
@@ -4893,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;
 };
 
@@ -5052,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 21d88ac..b490f7f 100644
--- a/neuralnetworks/1.2/types.t
+++ b/neuralnetworks/1.2/types.t
@@ -97,379 +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.
-     *
-     * 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}.
-     */
-    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
@@ -683,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
index 6959056..41281ee 100644
--- a/neuralnetworks/1.2/utils/Android.bp
+++ b/neuralnetworks/1.2/utils/Android.bp
@@ -14,11 +14,19 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_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/*"],
-    exclude_srcs: ["src/ExecutionBurst*"],
     local_include_dirs: ["include/nnapi/hal/1.2/"],
     export_include_dirs: ["include"],
     cflags: ["-Wthread-safety"],
@@ -32,10 +40,16 @@
         "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 {
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
index 6fd1337..272cee7 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
@@ -52,6 +52,7 @@
 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);
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
index 489f857..b4bef5e 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
@@ -71,7 +71,6 @@
     const std::string& getVersionString() const override;
     nn::Version getFeatureLevel() const override;
     nn::DeviceType getType() const override;
-    bool isUpdatable() 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;
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
index 5356a91..9669d8c0 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
@@ -14,23 +14,28 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_CONTROLLER_H
-#define ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_CONTROLLER_H
+#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/macros.h>
+#include <android-base/thread_annotations.h>
 #include <android/hardware/neuralnetworks/1.0/types.h>
-#include <android/hardware/neuralnetworks/1.1/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/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ProtectCallback.h>
 
 #include <atomic>
 #include <chrono>
+#include <functional>
 #include <map>
 #include <memory>
 #include <mutex>
@@ -39,147 +44,148 @@
 #include <utility>
 #include <vector>
 
-namespace android::nn {
+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.
+ * 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 {
-    DISALLOW_IMPLICIT_CONSTRUCTORS(ExecutionBurstController);
+class ExecutionBurstController final : public nn::IBurst {
+    struct PrivateConstructorTag {};
 
   public:
+    using FallbackFunction =
+            std::function<nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>(
+                    const nn::Request&, nn::MeasureTiming, const nn::OptionalTimePoint&,
+                    const nn::OptionalDuration&)>;
+
     /**
-     * NN runtime burst callback object and memory cache.
+     * NN runtime memory cache.
      *
-     * ExecutionBurstCallback associates a hidl_memory object with a slot number
-     * to be passed across FMQ. The ExecutionBurstServer can use this callback
-     * to retrieve this hidl_memory corresponding to the slot via HIDL.
+     * 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.
+     * 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 ExecutionBurstCallback : public hardware::neuralnetworks::V1_2::IBurstCallback {
-        DISALLOW_COPY_AND_ASSIGN(ExecutionBurstCallback);
+    class MemoryCache : public std::enable_shared_from_this<MemoryCache> {
+        struct PrivateConstructorTag {};
 
       public:
-        ExecutionBurstCallback() = default;
+        using Task = std::function<void()>;
+        using Cleanup = base::ScopeGuard<Task>;
+        using SharedCleanup = std::shared_ptr<const Cleanup>;
+        using WeakCleanup = std::weak_ptr<const Cleanup>;
 
-        hardware::Return<void> getMemories(const hardware::hidl_vec<int32_t>& slots,
-                                           getMemories_cb cb) override;
+        // Custom constructor to pre-allocate cache sizes.
+        MemoryCache();
 
         /**
-         * This function performs one of two different actions:
-         * 1) If a key corresponding to a memory resource is unrecognized by the
-         *    ExecutionBurstCallback object, the ExecutionBurstCallback object
-         *    will allocate a slot, bind the memory to the slot, and return the
-         *    slot identifier.
-         * 2) If a key corresponding to a memory resource is recognized by the
-         *    ExecutionBurstCallback object, the ExecutionBurstCallback object
-         *    will return the existing slot identifier.
+         * Add a burst context to the MemoryCache object.
          *
-         * @param memories Memory resources used in an inference.
-         * @param keys Unique identifiers where each element corresponds to a
-         *     memory resource element in "memories".
-         * @return Unique slot identifiers where each returned slot element
-         *     corresponds to a memory resource element in "memories".
+         * 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.
          */
-        std::vector<int32_t> getSlots(const hardware::hidl_vec<hardware::hidl_memory>& memories,
-                                      const std::vector<intptr_t>& keys);
+        void setBurstContext(sp<IBurstContext> burstContext);
 
-        /*
-         * This function performs two different actions:
-         * 1) Removes an entry from the cache (if present), including the local
-         *    storage of the hidl_memory object. Note that this call does not
-         *    free any corresponding hidl_memory object in ExecutionBurstServer,
-         *    which is separately freed via IBurstContext::freeMemory.
-         * 2) Return whether a cache entry was removed and which slot was removed if
-         *    found. If the key did not to correspond to any entry in the cache, a
-         *    slot number of 0 is returned. The slot number and whether the entry
-         *    existed is useful so the same slot can be freed in the
-         *    ExecutionBurstServer's cache via IBurstContext::freeMemory.
+        /**
+         * 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<bool, int32_t> freeMemory(intptr_t key);
+        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:
-        int32_t getSlotLocked(const hardware::hidl_memory& memory, intptr_t key);
-        int32_t allocateSlotLocked();
+        void freeMemory(const nn::SharedMemory& memory);
+        int32_t allocateSlotLocked() REQUIRES(mMutex);
 
         std::mutex mMutex;
-        std::stack<int32_t, std::vector<int32_t>> mFreeSlots;
-        std::map<intptr_t, int32_t> mMemoryIdToSlot;
-        std::vector<hardware::hidl_memory> mMemoryCache;
+        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.
      *
-     * Prefer this over ExecutionBurstController's constructor.
-     *
      * @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.
+     * @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 std::unique_ptr<ExecutionBurstController> create(
-            const sp<hardware::neuralnetworks::V1_2::IPreparedModel>& preparedModel,
+    static nn::GeneralResult<std::shared_ptr<const ExecutionBurstController>> create(
+            const sp<IPreparedModel>& preparedModel, FallbackFunction fallback,
             std::chrono::microseconds pollingTimeWindow);
 
-    // prefer calling ExecutionBurstController::create
-    ExecutionBurstController(const std::shared_ptr<RequestChannelSender>& requestChannelSender,
-                             const std::shared_ptr<ResultChannelReceiver>& resultChannelReceiver,
-                             const sp<hardware::neuralnetworks::V1_2::IBurstContext>& burstContext,
-                             const sp<ExecutionBurstCallback>& callback,
-                             const sp<hardware::hidl_death_recipient>& deathHandler = nullptr);
+    ExecutionBurstController(PrivateConstructorTag tag, FallbackFunction fallback,
+                             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);
 
-    // explicit destructor to unregister the death recipient
-    ~ExecutionBurstController();
+    // See IBurst::cacheMemory for information on this method.
+    OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
 
-    /**
-     * Execute a request on a model.
-     *
-     * @param request Arguments to be executed on a model.
-     * @param measure Whether to collect timing measurements, either YES or NO
-     * @param memoryIds Identifiers corresponding to each memory object in the
-     *     request's pools.
-     * @return A tuple of:
-     *     - result code of the execution
-     *     - dynamic output shapes from the execution
-     *     - any execution time measurements of the execution
-     *     - whether or not a failed burst execution should be re-run using a
-     *       different path (e.g., IPreparedModel::executeSynchronously)
-     */
-    std::tuple<int, std::vector<hardware::neuralnetworks::V1_2::OutputShape>,
-               hardware::neuralnetworks::V1_2::Timing, bool>
-    compute(const hardware::neuralnetworks::V1_0::Request& request,
-            hardware::neuralnetworks::V1_2::MeasureTiming measure,
-            const std::vector<intptr_t>& memoryIds);
-
-    /**
-     * Propagate a user's freeing of memory to the service.
-     *
-     * @param key Key corresponding to the memory object.
-     */
-    void freeMemory(intptr_t key);
+    // 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;
 
   private:
-    std::mutex mMutex;
-    const std::shared_ptr<RequestChannelSender> mRequestChannelSender;
-    const std::shared_ptr<ResultChannelReceiver> mResultChannelReceiver;
-    const sp<hardware::neuralnetworks::V1_2::IBurstContext> mBurstContext;
-    const sp<ExecutionBurstCallback> mMemoryCache;
-    const sp<hardware::hidl_death_recipient> mDeathHandler;
+    mutable std::atomic_flag mExecutionInFlight = ATOMIC_FLAG_INIT;
+    const FallbackFunction kFallback;
+    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::nn
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
 
-#endif  // ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_CONTROLLER_H
+#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
index 2e109b2..f7926f5 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h
@@ -14,19 +14,22 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_SERVER_H
-#define ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_SERVER_H
+#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/macros.h>
+#include <android-base/thread_annotations.h>
 #include <android/hardware/neuralnetworks/1.0/types.h>
-#include <android/hardware/neuralnetworks/1.1/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>
@@ -36,84 +39,61 @@
 #include <tuple>
 #include <vector>
 
-namespace android::nn {
+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.
+ * 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 hardware::neuralnetworks::V1_2::IBurstContext {
-    DISALLOW_IMPLICIT_CONSTRUCTORS(ExecutionBurstServer);
+class ExecutionBurstServer : public IBurstContext {
+    struct PrivateConstructorTag {};
 
   public:
     /**
-     * IBurstExecutorWithCache is a callback object passed to
-     * ExecutionBurstServer's factory function that is used to perform an
-     * execution. Because some memory resources are needed across multiple
-     * executions, this object also contains a local cache that can directly be
-     * used in the execution.
+     * Class to cache the memory objects for a burst object.
      *
-     * ExecutionBurstServer will never access its IBurstExecutorWithCache object
-     * with concurrent calls.
+     * This class is thread-safe.
      */
-    class IBurstExecutorWithCache {
-        DISALLOW_COPY_AND_ASSIGN(IBurstExecutorWithCache);
-
+    class MemoryCache {
       public:
-        IBurstExecutorWithCache() = default;
-        virtual ~IBurstExecutorWithCache() = default;
+        // Precondition: burstExecutor != nullptr
+        // Precondition: burstCallback != nullptr
+        MemoryCache(nn::SharedBurst burstExecutor, sp<IBurstCallback> burstCallback);
 
         /**
-         * Checks if a cache entry specified by a slot is present in the cache.
+         * Get the cached memory objects corresponding to provided slot identifiers.
          *
-         * @param slot Identifier of the cache entry.
-         * @return 'true' if the cache entry is present in the cache, 'false'
-         *     otherwise.
+         * 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.
          */
-        virtual bool isCacheEntryPresent(int32_t slot) const = 0;
+        nn::GeneralResult<std::vector<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>>>
+        getCacheEntries(const std::vector<int32_t>& slots);
 
         /**
-         * Adds an entry specified by a slot to the cache.
+         * Remove an entry from the cache.
          *
-         * The caller of this function must ensure that the cache entry that is
-         * being added is not already present in the cache. This can be checked
-         * via isCacheEntryPresent.
-         *
-         * @param memory Memory resource to be cached.
-         * @param slot Slot identifier corresponding to the memory resource.
+         * @param slot Identifier of the memory object to be removed from the cache.
          */
-        virtual void addCacheEntry(const hardware::hidl_memory& memory, int32_t slot) = 0;
+        void removeCacheEntry(int32_t slot);
 
-        /**
-         * Removes an entry specified by a slot from the cache.
-         *
-         * If the cache entry corresponding to the slot number does not exist,
-         * the call does nothing.
-         *
-         * @param slot Slot identifier corresponding to the memory resource.
-         */
-        virtual void removeCacheEntry(int32_t slot) = 0;
+      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);
 
-        /**
-         * Perform an execution.
-         *
-         * @param request Request object with inputs and outputs specified.
-         *     Request::pools is empty, and DataLocation::poolIndex instead
-         *     refers to the 'slots' argument as if it were Request::pools.
-         * @param slots Slots corresponding to the cached memory entries to be
-         *     used.
-         * @param measure Whether timing information is requested for the
-         *     execution.
-         * @return Result of the execution, including the status of the
-         *     execution, dynamic output shapes, and any timing information.
-         */
-        virtual std::tuple<hardware::neuralnetworks::V1_0::ErrorStatus,
-                           hardware::hidl_vec<hardware::neuralnetworks::V1_2::OutputShape>,
-                           hardware::neuralnetworks::V1_2::Timing>
-        execute(const hardware::neuralnetworks::V1_0::Request& request,
-                const std::vector<int32_t>& slots,
-                hardware::neuralnetworks::V1_2::MeasureTiming measure) = 0;
+        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;
     };
 
     /**
@@ -124,85 +104,52 @@
      * 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 executorWithCache 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.
-     * @result IBurstContext Handle to the burst context.
-     */
-    static sp<ExecutionBurstServer> create(
-            const sp<hardware::neuralnetworks::V1_2::IBurstCallback>& callback,
-            const FmqRequestDescriptor& requestChannel, const FmqResultDescriptor& resultChannel,
-            std::shared_ptr<IBurstExecutorWithCache> executorWithCache,
-            std::chrono::microseconds pollingTimeWindow = std::chrono::microseconds{0});
-
-    /**
-     * 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 preparedModel PreparedModel that the burst object was created from.
-     *     IPreparedModel::executeSynchronously will be used to perform the
+     * @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 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.
-     * @result IBurstContext Handle to the burst context.
+     * @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 sp<ExecutionBurstServer> create(
-            const sp<hardware::neuralnetworks::V1_2::IBurstCallback>& callback,
-            const FmqRequestDescriptor& requestChannel, const FmqResultDescriptor& resultChannel,
-            hardware::neuralnetworks::V1_2::IPreparedModel* preparedModel,
+    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(const sp<hardware::neuralnetworks::V1_2::IBurstCallback>& callback,
+    ExecutionBurstServer(PrivateConstructorTag tag, const sp<IBurstCallback>& callback,
                          std::unique_ptr<RequestChannelReceiver> requestChannel,
                          std::unique_ptr<ResultChannelSender> resultChannel,
-                         std::shared_ptr<IBurstExecutorWithCache> cachedExecutor);
+                         nn::SharedBurst burstExecutor);
     ~ExecutionBurstServer();
 
-    // Used by the NN runtime to preemptively remove any stored memory.
-    hardware::Return<void> freeMemory(int32_t slot) override;
+    // 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:
-    // Ensures all cache entries contained in mExecutorWithCache are present in
-    // the cache. If they are not present, they are retrieved (via
-    // IBurstCallback::getMemories) and added to mExecutorWithCache.
-    //
-    // This method is locked via mMutex when it is called.
-    void ensureCacheEntriesArePresentLocked(const std::vector<int32_t>& slots);
-
-    // Work loop that will continue processing execution requests until the
-    // ExecutionBurstServer object is freed.
+    // 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::mutex mMutex;
     std::atomic<bool> mTeardown{false};
-    const sp<hardware::neuralnetworks::V1_2::IBurstCallback> mCallback;
+    const sp<IBurstCallback> mCallback;
     const std::unique_ptr<RequestChannelReceiver> mRequestChannelReceiver;
     const std::unique_ptr<ResultChannelSender> mResultChannelSender;
-    const std::shared_ptr<IBurstExecutorWithCache> mExecutorWithCache;
+    const nn::SharedBurst mBurstExecutor;
+    MemoryCache mMemoryCache;
 };
 
-}  // namespace android::nn
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
 
-#endif  // ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_SERVER_H
+#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
index 8a41591..c662bc3 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h
@@ -18,15 +18,16 @@
 #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.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 <optional>
 #include <tuple>
 #include <utility>
 #include <vector>
@@ -38,159 +39,139 @@
  */
 constexpr const size_t kExecutionBurstChannelLength = 1024;
 
-using FmqRequestDescriptor = MQDescriptorSync<FmqRequestDatum>;
-using FmqResultDescriptor = MQDescriptorSync<FmqResultDatum>;
+/**
+ * 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.
  *
- * Prefer calling RequestChannelSender::send.
- *
  * @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.
+ * @param memoryIds Slot identifiers corresponding to memory resources for the request.
  * @return Serialized FMQ request data.
  */
-std::vector<hardware::neuralnetworks::V1_2::FmqRequestDatum> serialize(
-        const hardware::neuralnetworks::V1_0::Request& request,
-        hardware::neuralnetworks::V1_2::MeasureTiming measure, const std::vector<int32_t>& slots);
+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.
+ * 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, std::nullopt otherwise.
+ * @return Request object if successfully deserialized, otherwise an error message.
  */
-std::optional<std::tuple<hardware::neuralnetworks::V1_0::Request, std::vector<int32_t>,
-                         hardware::neuralnetworks::V1_2::MeasureTiming>>
-deserialize(const std::vector<hardware::neuralnetworks::V1_2::FmqRequestDatum>& data);
+nn::Result<std::tuple<V1_0::Request, std::vector<int32_t>, MeasureTiming>> deserialize(
+        const std::vector<FmqRequestDatum>& data);
 
 /**
  * Function to serialize results.
  *
- * Prefer calling ResultChannelSender::send.
- *
  * @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<hardware::neuralnetworks::V1_2::FmqResultDatum> serialize(
-        hardware::neuralnetworks::V1_0::ErrorStatus errorStatus,
-        const std::vector<hardware::neuralnetworks::V1_2::OutputShape>& outputShapes,
-        hardware::neuralnetworks::V1_2::Timing timing);
+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.
+ * 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, std::nullopt otherwise.
+ * @return Result object if successfully deserialized, otherwise an error message.
  */
-std::optional<std::tuple<hardware::neuralnetworks::V1_0::ErrorStatus,
-                         std::vector<hardware::neuralnetworks::V1_2::OutputShape>,
-                         hardware::neuralnetworks::V1_2::Timing>>
-deserialize(const std::vector<hardware::neuralnetworks::V1_2::FmqResultDatum>& data);
+nn::Result<std::tuple<V1_0::ErrorStatus, std::vector<OutputShape>, Timing>> deserialize(
+        const std::vector<FmqResultDatum>& data);
 
 /**
- * Convert result code to error status.
- *
- * @param resultCode Result code to be converted.
- * @return ErrorStatus Resultant error status.
+ * RequestChannelSender is responsible for serializing the result packet of information, sending it
+ * on the result channel, and signaling that the data is available.
  */
-hardware::neuralnetworks::V1_0::ErrorStatus legacyConvertResultCodeToErrorStatus(int resultCode);
-
-/**
- * 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 {
-    using FmqRequestDescriptor =
-            hardware::MQDescriptorSync<hardware::neuralnetworks::V1_2::FmqRequestDatum>;
-    using FmqRequestChannel =
-            hardware::MessageQueue<hardware::neuralnetworks::V1_2::FmqRequestDatum,
-                                   hardware::kSynchronizedReadWrite>;
+class RequestChannelSender final : public neuralnetworks::utils::IProtectedCallback {
+    struct PrivateConstructorTag {};
 
   public:
     /**
      * Create the sending end of a request channel.
      *
-     * Prefer this call over the constructor.
-     *
      * @param channelLength Number of elements in the FMQ.
-     * @return A pair of ResultChannelReceiver and the FMQ descriptor on
-     *     successful creation, both nullptr otherwise.
+     * @return A pair of ResultChannelReceiver and the FMQ descriptor on successful creation,
+     *     GeneralError otherwise.
      */
-    static std::pair<std::unique_ptr<RequestChannelSender>, const FmqRequestDescriptor*> create(
-            size_t channelLength);
+    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 memoryIds Slot identifiers corresponding to memory resources for
-     *     the request.
-     * @return 'true' on successful send, 'false' otherwise.
+     * @param slots Slot identifiers corresponding to memory resources for the request.
+     * @return An empty `Result` on successful send, otherwise an error message.
      */
-    bool send(const hardware::neuralnetworks::V1_0::Request& request,
-              hardware::neuralnetworks::V1_2::MeasureTiming measure,
-              const std::vector<int32_t>& slots);
+    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.
+     * 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 invalidate();
+    void notifyAsDeadObject() override;
 
     // prefer calling RequestChannelSender::send
-    bool sendPacket(const std::vector<hardware::neuralnetworks::V1_2::FmqRequestDatum>& packet);
+    nn::Result<void> sendPacket(const std::vector<FmqRequestDatum>& packet);
 
-    RequestChannelSender(std::unique_ptr<FmqRequestChannel> fmqRequestChannel);
+    RequestChannelSender(PrivateConstructorTag tag, size_t channelLength);
 
   private:
-    const std::unique_ptr<FmqRequestChannel> mFmqRequestChannel;
+    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.
+ * 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.
+ * 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 {
-    using FmqRequestChannel =
-            hardware::MessageQueue<hardware::neuralnetworks::V1_2::FmqRequestDatum,
-                                   hardware::kSynchronizedReadWrite>;
+class RequestChannelReceiver final {
+    struct PrivateConstructorTag {};
 
   public:
     /**
      * Create the receiving end of a request channel.
      *
-     * Prefer this call over the constructor.
-     *
      * @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.
+     * @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 std::unique_ptr<RequestChannelReceiver> create(
-            const FmqRequestDescriptor& requestChannel,
+    static nn::GeneralResult<std::unique_ptr<RequestChannelReceiver>> create(
+            const MQDescriptorSync<FmqRequestDatum>& requestChannel,
             std::chrono::microseconds pollingTimeWindow);
 
     /**
@@ -200,49 +181,45 @@
      * 1) The packet has been retrieved, or
      * 2) The receiver has been invalidated
      *
-     * @return Request object if successfully received, std::nullopt if error or
-     *     if the receiver object was invalidated.
+     * @return Request object if successfully received, an appropriate message if error or if the
+     *     receiver object was invalidated.
      */
-    std::optional<std::tuple<hardware::neuralnetworks::V1_0::Request, std::vector<int32_t>,
-                             hardware::neuralnetworks::V1_2::MeasureTiming>>
-    getBlocking();
+    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.
+     * Method to mark the channel as invalid, unblocking any current or future calls to
+     * RequestChannelReceiver::getBlocking.
      */
     void invalidate();
 
-    RequestChannelReceiver(std::unique_ptr<FmqRequestChannel> fmqRequestChannel,
+    RequestChannelReceiver(PrivateConstructorTag tag,
+                           const MQDescriptorSync<FmqRequestDatum>& requestChannel,
                            std::chrono::microseconds pollingTimeWindow);
 
   private:
-    std::optional<std::vector<hardware::neuralnetworks::V1_2::FmqRequestDatum>> getPacketBlocking();
+    nn::Result<std::vector<FmqRequestDatum>> getPacketBlocking();
 
-    const std::unique_ptr<FmqRequestChannel> mFmqRequestChannel;
+    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.
+ * 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 {
-    using FmqResultChannel = hardware::MessageQueue<hardware::neuralnetworks::V1_2::FmqResultDatum,
-                                                    hardware::kSynchronizedReadWrite>;
+class ResultChannelSender final {
+    struct PrivateConstructorTag {};
 
   public:
     /**
      * Create the sending end of a result channel.
      *
-     * Prefer this call over the constructor.
-     *
      * @param resultChannel Descriptor for the result channel.
      * @return ResultChannelSender on successful creation, nullptr otherwise.
      */
-    static std::unique_ptr<ResultChannelSender> create(const FmqResultDescriptor& resultChannel);
+    static nn::GeneralResult<std::unique_ptr<ResultChannelSender>> create(
+            const MQDescriptorSync<FmqResultDatum>& resultChannel);
 
     /**
      * Send the result to the channel.
@@ -250,52 +227,44 @@
      * @param errorStatus Status of the execution.
      * @param outputShapes Dynamic shapes of the output tensors.
      * @param timing Timing information of the execution.
-     * @return 'true' on successful send, 'false' otherwise.
      */
-    bool send(hardware::neuralnetworks::V1_0::ErrorStatus errorStatus,
-              const std::vector<hardware::neuralnetworks::V1_2::OutputShape>& outputShapes,
-              hardware::neuralnetworks::V1_2::Timing timing);
+    void send(V1_0::ErrorStatus errorStatus, const std::vector<OutputShape>& outputShapes,
+              Timing timing);
 
     // prefer calling ResultChannelSender::send
-    bool sendPacket(const std::vector<hardware::neuralnetworks::V1_2::FmqResultDatum>& packet);
+    void sendPacket(const std::vector<FmqResultDatum>& packet);
 
-    ResultChannelSender(std::unique_ptr<FmqResultChannel> fmqResultChannel);
+    ResultChannelSender(PrivateConstructorTag tag,
+                        const MQDescriptorSync<FmqResultDatum>& resultChannel);
 
   private:
-    const std::unique_ptr<FmqResultChannel> mFmqResultChannel;
+    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.
+ * 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.
+ * 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 {
-    using FmqResultDescriptor =
-            hardware::MQDescriptorSync<hardware::neuralnetworks::V1_2::FmqResultDatum>;
-    using FmqResultChannel = hardware::MessageQueue<hardware::neuralnetworks::V1_2::FmqResultDatum,
-                                                    hardware::kSynchronizedReadWrite>;
+class ResultChannelReceiver final : public neuralnetworks::utils::IProtectedCallback {
+    struct PrivateConstructorTag {};
 
   public:
     /**
      * Create the receiving end of a result channel.
      *
-     * Prefer this call over the constructor.
-     *
      * @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, both nullptr otherwise.
+     * @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 std::pair<std::unique_ptr<ResultChannelReceiver>, const FmqResultDescriptor*> create(
-            size_t channelLength, std::chrono::microseconds pollingTimeWindow);
+    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.
@@ -304,28 +273,25 @@
      * 1) The packet has been retrieved, or
      * 2) The receiver has been invalidated
      *
-     * @return Result object if successfully received, std::nullopt if error or
+     * @return Result object if successfully received, otherwise an appropriate message if error or
      *     if the receiver object was invalidated.
      */
-    std::optional<std::tuple<hardware::neuralnetworks::V1_0::ErrorStatus,
-                             std::vector<hardware::neuralnetworks::V1_2::OutputShape>,
-                             hardware::neuralnetworks::V1_2::Timing>>
-    getBlocking();
+    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.
+     * Method to mark the channel as invalid, unblocking any current or future calls to
+     * ResultChannelReceiver::getBlocking.
      */
-    void invalidate();
+    void notifyAsDeadObject() override;
 
     // prefer calling ResultChannelReceiver::getBlocking
-    std::optional<std::vector<hardware::neuralnetworks::V1_2::FmqResultDatum>> getPacketBlocking();
+    nn::Result<std::vector<FmqResultDatum>> getPacketBlocking();
 
-    ResultChannelReceiver(std::unique_ptr<FmqResultChannel> fmqResultChannel,
+    ResultChannelReceiver(PrivateConstructorTag tag, size_t channelLength,
                           std::chrono::microseconds pollingTimeWindow);
 
   private:
-    const std::unique_ptr<FmqResultChannel> mFmqResultChannel;
+    MessageQueue<FmqResultDatum, kSynchronizedReadWrite> mFmqResultChannel;
     std::atomic<bool> mValid{true};
     const std::chrono::microseconds kPollingTimeWindow;
 };
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
index c289fc8..09691b6 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
@@ -22,19 +22,25 @@
 #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) {
@@ -54,6 +60,21 @@
     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/Callbacks.cpp b/neuralnetworks/1.2/utils/src/Callbacks.cpp
index fefa122..9f54bb1 100644
--- a/neuralnetworks/1.2/utils/src/Callbacks.cpp
+++ b/neuralnetworks/1.2/utils/src/Callbacks.cpp
@@ -43,6 +43,15 @@
 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) {
@@ -72,7 +81,7 @@
 
 Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus status,
                                            const sp<V1_0::IPreparedModel>& preparedModel) {
-    mData.put(V1_0::utils::prepareModelCallback(status, preparedModel));
+    mData.put(prepareModelCallback(status, preparedModel));
     return Void();
 }
 
diff --git a/neuralnetworks/1.2/utils/src/Conversions.cpp b/neuralnetworks/1.2/utils/src/Conversions.cpp
index 062f6f7..29945b7 100644
--- a/neuralnetworks/1.2/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.2/utils/src/Conversions.cpp
@@ -37,6 +37,8 @@
 #include <type_traits>
 #include <utility>
 
+#include "Utils.h"
+
 namespace {
 
 template <typename Type>
@@ -45,50 +47,23 @@
 }
 
 using HalDuration = std::chrono::duration<uint64_t, std::micro>;
-constexpr auto kVersion = android::nn::Version::ANDROID_Q;
-constexpr uint64_t kNoTiming = std::numeric_limits<uint64_t>::max();
 
 }  // namespace
 
 namespace android::nn {
 namespace {
 
-constexpr bool validOperandType(OperandType operandType) {
-    switch (operandType) {
-        case OperandType::FLOAT32:
-        case OperandType::INT32:
-        case OperandType::UINT32:
-        case OperandType::TENSOR_FLOAT32:
-        case OperandType::TENSOR_INT32:
-        case OperandType::TENSOR_QUANT8_ASYMM:
-        case OperandType::BOOL:
-        case OperandType::TENSOR_QUANT16_SYMM:
-        case OperandType::TENSOR_FLOAT16:
-        case OperandType::TENSOR_BOOL8:
-        case OperandType::FLOAT16:
-        case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
-        case OperandType::TENSOR_QUANT16_ASYMM:
-        case OperandType::TENSOR_QUANT8_SYMM:
-        case OperandType::OEM:
-        case OperandType::TENSOR_OEM_BYTE:
-            return true;
-        default:
-            break;
-    }
-    return isExtension(operandType);
-}
-
 using hardware::hidl_handle;
 using hardware::hidl_vec;
 
 template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
         std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
         const hidl_vec<Type>& arguments) {
-    std::vector<unvalidatedConvertOutput<Type>> canonical;
+    std::vector<UnvalidatedConvertOutput<Type>> canonical;
     canonical.reserve(arguments.size());
     for (const auto& argument : arguments) {
         canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
@@ -97,29 +72,16 @@
 }
 
 template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
-        const hidl_vec<Type>& arguments) {
-    return unvalidatedConvertVec(arguments);
-}
-
-template <typename Type>
-decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
     auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
-    const auto maybeVersion = validate(canonical);
-    if (!maybeVersion.has_value()) {
-        return error() << maybeVersion.error();
-    }
-    const auto version = maybeVersion.value();
-    if (version > kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
-    }
+    NN_TRY(hal::V1_2::utils::compliantVersion(canonical));
     return canonical;
 }
 
 template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> validatedConvert(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> validatedConvert(
         const hidl_vec<Type>& arguments) {
-    std::vector<unvalidatedConvertOutput<Type>> canonical;
+    std::vector<UnvalidatedConvertOutput<Type>> canonical;
     canonical.reserve(arguments.size());
     for (const auto& argument : arguments) {
         canonical.push_back(NN_TRY(validatedConvert(argument)));
@@ -145,8 +107,7 @@
     const bool validOperandTypes = std::all_of(
             capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
             [](const hal::V1_2::Capabilities::OperandPerformance& operandPerformance) {
-                const auto maybeType = unvalidatedConvert(operandPerformance.type);
-                return !maybeType.has_value() ? false : validOperandType(maybeType.value());
+                return validatedConvert(operandPerformance.type).has_value();
             });
     if (!validOperandTypes) {
         return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
@@ -227,7 +188,7 @@
 
     // Verify number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(model.operands.size(), operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(model.operands.size(), operations));
     CHECK(model.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < model.operands.size(); ++i) {
         if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -275,6 +236,7 @@
 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 {};
         }
@@ -304,7 +266,11 @@
 }
 
 GeneralResult<SharedHandle> unvalidatedConvert(const hidl_handle& hidlHandle) {
-    return hal::utils::sharedHandleFromNativeHandle(hidlHandle.getNativeHandle());
+    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) {
@@ -327,6 +293,10 @@
     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);
 }
@@ -365,30 +335,24 @@
     return V1_0::utils::unvalidatedConvert(operandValues);
 }
 
-nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
     return V1_0::utils::unvalidatedConvert(memory);
 }
 
 template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
         std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
         const std::vector<Type>& arguments) {
-    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+    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<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
-        const std::vector<Type>& arguments) {
-    return unvalidatedConvertVec(arguments);
-}
-
 nn::GeneralResult<Operand::ExtraParams> makeExtraParams(nn::Operand::NoParams /*noParams*/) {
     return Operand::ExtraParams{};
 }
@@ -408,22 +372,15 @@
 }
 
 template <typename Type>
-decltype(utils::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
-    const auto maybeVersion = nn::validate(canonical);
-    if (!maybeVersion.has_value()) {
-        return nn::error() << maybeVersion.error();
-    }
-    const auto version = maybeVersion.value();
-    if (version > kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
-    }
-    return utils::unvalidatedConvert(canonical);
+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(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> validatedConvert(
         const std::vector<Type>& arguments) {
-    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+    hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
     for (size_t i = 0; i < arguments.size(); ++i) {
         halObject[i] = NN_TRY(validatedConvert(arguments[i]));
     }
@@ -461,7 +418,7 @@
                  capabilities.operandPerformance.asVector().end(),
                  std::back_inserter(operandPerformance),
                  [](const nn::Capabilities::OperandPerformance& operandPerformance) {
-                     return nn::validOperandType(operandPerformance.type);
+                     return compliantVersion(operandPerformance.type).has_value();
                  });
 
     return Capabilities{
@@ -525,7 +482,7 @@
 
     // Update number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(operands.size(), model.main.operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), model.main.operations));
     CHECK(operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < operands.size(); ++i) {
         operands[i].numberOfConsumers = numberOfConsumers[i];
@@ -562,6 +519,7 @@
 
 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;
         }
@@ -588,7 +546,10 @@
 }
 
 nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
-    return hal::utils::hidlHandleFromSharedHandle(handle);
+    if (handle == nullptr) {
+        return {};
+    }
+    return hal::utils::hidlHandleFromSharedHandle(*handle);
 }
 
 nn::GeneralResult<DeviceType> convert(const nn::DeviceType& deviceType) {
diff --git a/neuralnetworks/1.2/utils/src/Device.cpp b/neuralnetworks/1.2/utils/src/Device.cpp
index 1954dfa..9fe0de2 100644
--- a/neuralnetworks/1.2/utils/src/Device.cpp
+++ b/neuralnetworks/1.2/utils/src/Device.cpp
@@ -199,10 +199,6 @@
     return kDeviceType;
 }
 
-bool Device::isUpdatable() const {
-    return false;
-}
-
 const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
     return kExtensions;
 }
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
index 2265861..7a17f25 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
@@ -17,283 +17,323 @@
 #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/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 "ExecutionBurstUtils.h"
-#include "HalInterfaces.h"
+#include "Callbacks.h"
+#include "Conversions.h"
 #include "Tracing.h"
 #include "Utils.h"
 
-namespace android::nn {
+namespace android::hardware::neuralnetworks::V1_2::utils {
 namespace {
 
-class BurstContextDeathHandler : public hardware::hidl_death_recipient {
-  public:
-    using Callback = std::function<void()>;
-
-    BurstContextDeathHandler(const Callback& onDeathCallback) : mOnDeathCallback(onDeathCallback) {
-        CHECK(onDeathCallback != nullptr);
+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";
     }
-
-    void serviceDied(uint64_t /*cookie*/, const wp<hidl::base::V1_0::IBase>& /*who*/) override {
-        LOG(ERROR) << "BurstContextDeathHandler::serviceDied -- service unexpectedly died!";
-        mOnDeathCallback();
-    }
-
-  private:
-    const Callback mOnDeathCallback;
-};
-
-}  // anonymous namespace
-
-hardware::Return<void> ExecutionBurstController::ExecutionBurstCallback::getMemories(
-        const hardware::hidl_vec<int32_t>& slots, getMemories_cb cb) {
-    std::lock_guard<std::mutex> guard(mMutex);
-
-    // get all memories
-    hardware::hidl_vec<hardware::hidl_memory> memories(slots.size());
-    std::transform(slots.begin(), slots.end(), memories.begin(), [this](int32_t slot) {
-        return slot < mMemoryCache.size() ? mMemoryCache[slot] : hardware::hidl_memory{};
-    });
-
-    // ensure all memories are valid
-    if (!std::all_of(memories.begin(), memories.end(),
-                     [](const hardware::hidl_memory& memory) { return memory.valid(); })) {
-        cb(V1_0::ErrorStatus::INVALID_ARGUMENT, {});
-        return hardware::Void();
-    }
-
-    // return successful
-    cb(V1_0::ErrorStatus::NONE, std::move(memories));
-    return hardware::Void();
+    return burstContext;
 }
 
-std::vector<int32_t> ExecutionBurstController::ExecutionBurstCallback::getSlots(
-        const hardware::hidl_vec<hardware::hidl_memory>& memories,
-        const std::vector<intptr_t>& keys) {
-    std::lock_guard<std::mutex> guard(mMutex);
-
-    // retrieve (or bind) all slots corresponding to memories
-    std::vector<int32_t> slots;
-    slots.reserve(memories.size());
-    for (size_t i = 0; i < memories.size(); ++i) {
-        slots.push_back(getSlotLocked(memories[i], keys[i]));
+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 slots;
+    return memories;
 }
 
-std::pair<bool, int32_t> ExecutionBurstController::ExecutionBurstCallback::freeMemory(
-        intptr_t key) {
-    std::lock_guard<std::mutex> guard(mMutex);
+}  // namespace
 
-    auto iter = mMemoryIdToSlot.find(key);
-    if (iter == mMemoryIdToSlot.end()) {
-        return {false, 0};
-    }
-    const int32_t slot = iter->second;
-    mMemoryIdToSlot.erase(key);
-    mMemoryCache[slot] = {};
-    mFreeSlots.push(slot);
-    return {true, slot};
+// 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);
 }
 
-int32_t ExecutionBurstController::ExecutionBurstCallback::getSlotLocked(
-        const hardware::hidl_memory& memory, intptr_t key) {
-    auto iter = mMemoryIdToSlot.find(key);
-    if (iter == mMemoryIdToSlot.end()) {
-        const int32_t slot = allocateSlotLocked();
-        mMemoryIdToSlot[key] = slot;
-        mMemoryCache[slot] = memory;
-        return slot;
-    } else {
+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;
-        return slot;
+        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));
 }
 
-int32_t ExecutionBurstController::ExecutionBurstCallback::allocateSlotLocked() {
+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.size() > 0) {
+    // If there is a free slot, use it.
+    if (!mFreeSlots.empty()) {
         const int32_t slot = mFreeSlots.top();
         mFreeSlots.pop();
         return slot;
     }
 
-    // otherwise use a slot for the first time
-    CHECK(mMemoryCache.size() < kMaxNumberOfSlots) << "Exceeded maximum number of slots!";
+    // 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;
 }
 
-std::unique_ptr<ExecutionBurstController> ExecutionBurstController::create(
-        const sp<V1_2::IPreparedModel>& preparedModel,
+// 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(
+        const sp<V1_2::IPreparedModel>& preparedModel, FallbackFunction fallback,
         std::chrono::microseconds pollingTimeWindow) {
     // check inputs
     if (preparedModel == nullptr) {
-        LOG(ERROR) << "ExecutionBurstController::create passed a nullptr";
-        return nullptr;
+        return NN_ERROR() << "ExecutionBurstController::create passed a nullptr";
     }
 
-    // create callback object
-    sp<ExecutionBurstCallback> callback = new ExecutionBurstCallback();
-
     // create FMQ objects
-    auto [requestChannelSenderTemp, requestChannelDescriptor] =
-            RequestChannelSender::create(kExecutionBurstChannelLength);
-    auto [resultChannelReceiverTemp, resultChannelDescriptor] =
-            ResultChannelReceiver::create(kExecutionBurstChannelLength, pollingTimeWindow);
-    std::shared_ptr<RequestChannelSender> requestChannelSender =
-            std::move(requestChannelSenderTemp);
-    std::shared_ptr<ResultChannelReceiver> resultChannelReceiver =
-            std::move(resultChannelReceiverTemp);
+    auto [requestChannelSender, requestChannelDescriptor] =
+            NN_TRY(RequestChannelSender::create(kExecutionBurstChannelLength));
+    auto [resultChannelReceiver, resultChannelDescriptor] =
+            NN_TRY(ResultChannelReceiver::create(kExecutionBurstChannelLength, pollingTimeWindow));
 
     // check FMQ objects
-    if (!requestChannelSender || !resultChannelReceiver || !requestChannelDescriptor ||
-        !resultChannelDescriptor) {
-        LOG(ERROR) << "ExecutionBurstController::create failed to create FastMessageQueue";
-        return nullptr;
-    }
+    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
-    V1_0::ErrorStatus errorStatus;
-    sp<IBurstContext> burstContext;
-    const hardware::Return<void> ret = preparedModel->configureExecutionBurst(
-            callback, *requestChannelDescriptor, *resultChannelDescriptor,
-            [&errorStatus, &burstContext](V1_0::ErrorStatus status,
-                                          const sp<IBurstContext>& context) {
-                errorStatus = status;
-                burstContext = context;
-            });
+    const Return<void> ret = preparedModel->configureExecutionBurst(
+            burstCallback, *requestChannelDescriptor, *resultChannelDescriptor, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
 
-    // check burst
-    if (!ret.isOk()) {
-        LOG(ERROR) << "IPreparedModel::configureExecutionBurst failed with description "
-                   << ret.description();
-        return nullptr;
-    }
-    if (errorStatus != V1_0::ErrorStatus::NONE) {
-        LOG(ERROR) << "IPreparedModel::configureExecutionBurst failed with status "
-                   << toString(errorStatus);
-        return nullptr;
-    }
-    if (burstContext == nullptr) {
-        LOG(ERROR) << "IPreparedModel::configureExecutionBurst returned nullptr for burst";
-        return nullptr;
-    }
+    auto burstContext = NN_TRY(cb.take());
+    memoryCache->setBurstContext(burstContext);
 
     // create death handler object
-    BurstContextDeathHandler::Callback onDeathCallback = [requestChannelSender,
-                                                          resultChannelReceiver] {
-        requestChannelSender->invalidate();
-        resultChannelReceiver->invalidate();
-    };
-    const sp<BurstContextDeathHandler> deathHandler = new BurstContextDeathHandler(onDeathCallback);
-
-    // linkToDeath registers a callback that will be invoked on service death to
-    // proactively handle service crashes. If the linkToDeath call fails,
-    // asynchronous calls are susceptible to hangs if the service crashes before
-    // providing the response.
-    const hardware::Return<bool> deathHandlerRet = burstContext->linkToDeath(deathHandler, 0);
-    if (!deathHandlerRet.isOk() || deathHandlerRet != true) {
-        LOG(ERROR) << "ExecutionBurstController::create -- Failed to register a death recipient "
-                      "for the IBurstContext object.";
-        return nullptr;
-    }
+    auto deathHandler = NN_TRY(neuralnetworks::utils::DeathHandler::create(burstContext));
+    deathHandler.protectCallbackForLifetimeOfDeathHandler(requestChannelSender.get());
+    deathHandler.protectCallbackForLifetimeOfDeathHandler(resultChannelReceiver.get());
 
     // make and return controller
-    return std::make_unique<ExecutionBurstController>(requestChannelSender, resultChannelReceiver,
-                                                      burstContext, callback, deathHandler);
+    return std::make_shared<const ExecutionBurstController>(
+            PrivateConstructorTag{}, std::move(fallback), std::move(requestChannelSender),
+            std::move(resultChannelReceiver), std::move(burstCallback), std::move(burstContext),
+            std::move(memoryCache), std::move(deathHandler));
 }
 
 ExecutionBurstController::ExecutionBurstController(
-        const std::shared_ptr<RequestChannelSender>& requestChannelSender,
-        const std::shared_ptr<ResultChannelReceiver>& resultChannelReceiver,
-        const sp<IBurstContext>& burstContext, const sp<ExecutionBurstCallback>& callback,
-        const sp<hardware::hidl_death_recipient>& deathHandler)
-    : mRequestChannelSender(requestChannelSender),
-      mResultChannelReceiver(resultChannelReceiver),
-      mBurstContext(burstContext),
-      mMemoryCache(callback),
-      mDeathHandler(deathHandler) {}
+        PrivateConstructorTag /*tag*/, FallbackFunction fallback,
+        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)
+    : kFallback(std::move(fallback)),
+      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::~ExecutionBurstController() {
-    // It is safe to ignore any errors resulting from this unlinkToDeath call
-    // because the ExecutionBurstController object is already being destroyed
-    // and its underlying IBurstContext object is no longer being used by the NN
-    // runtime.
-    if (mDeathHandler) {
-        mBurstContext->unlinkToDeath(mDeathHandler).isOk();
+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_FULL(NNTRACE_LAYER_IPC, 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
+        if (kFallback) {
+            return kFallback(request, measure, deadline, loopTimeoutDuration);
+        }
+        return NN_ERROR() << "Request object has features not supported by IBurst::execute";
     }
-}
 
-static std::tuple<int, std::vector<V1_2::OutputShape>, V1_2::Timing, bool> getExecutionResult(
-        V1_0::ErrorStatus status, std::vector<V1_2::OutputShape> outputShapes, V1_2::Timing timing,
-        bool fallback) {
-    auto [n, checkedOutputShapes, checkedTiming] =
-            getExecutionResult(convertToV1_3(status), std::move(outputShapes), timing);
-    return {n, convertToV1_2(checkedOutputShapes), convertToV1_2(checkedTiming), fallback};
-}
+    // clear pools field of request, as they will be provided via slots
+    const auto requestWithoutPools =
+            nn::Request{.inputs = request.inputs, .outputs = request.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::tuple<int, std::vector<V1_2::OutputShape>, V1_2::Timing, bool>
-ExecutionBurstController::compute(const V1_0::Request& request, V1_2::MeasureTiming measure,
-                                  const std::vector<intptr_t>& memoryIds) {
-    // 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_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION, "ExecutionBurstController::compute");
+    // 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(); });
 
-    std::lock_guard<std::mutex> guard(mMutex);
+    std::vector<int32_t> slots;
+    std::vector<OptionalCacheHold> holds;
+    slots.reserve(request.pools.size());
+    holds.reserve(request.pools.size());
+    for (const auto& memoryPool : request.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 std::vector<int32_t> slots = mMemoryCache->getSlots(request.pools, memoryIds);
-    const bool success = mRequestChannelSender->send(request, measure, slots);
-    if (!success) {
-        LOG(ERROR) << "Error sending FMQ packet";
-        // only use fallback execution path if the packet could not be sent
-        return getExecutionResult(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming12,
-                                  /*fallback=*/true);
+    const auto sendStatus = mRequestChannelSender->send(hidlRequest, hidlMeasure, slots);
+    if (!sendStatus.ok()) {
+        // fallback to another execution path if the packet could not be sent
+        if (kFallback) {
+            return kFallback(request, measure, deadline, loopTimeoutDuration);
+        }
+        return NN_ERROR() << "Error sending FMQ packet: " << sendStatus.error();
     }
 
     // get result packet
-    const auto result = mResultChannelReceiver->getBlocking();
-    if (!result) {
-        LOG(ERROR) << "Error retrieving FMQ packet";
-        // only use fallback execution path if the packet could not be sent
-        return getExecutionResult(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming12,
-                                  /*fallback=*/false);
-    }
-
-    // unpack results and return (only use fallback execution path if the
-    // packet could not be sent)
-    auto [status, outputShapes, timing] = std::move(*result);
-    return getExecutionResult(status, std::move(outputShapes), timing, /*fallback=*/false);
+    const auto [status, outputShapes, timing] =
+            NN_TRY(hal::utils::makeExecutionFailure(mResultChannelReceiver->getBlocking()));
+    return executionCallback(status, outputShapes, timing);
 }
 
-void ExecutionBurstController::freeMemory(intptr_t key) {
-    std::lock_guard<std::mutex> guard(mMutex);
-
-    bool valid;
-    int32_t slot;
-    std::tie(valid, slot) = mMemoryCache->freeMemory(key);
-    if (valid) {
-        mBurstContext->freeMemory(slot).isOk();
-    }
-}
-
-}  // namespace android::nn
+}  // 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
index 022548d..c67159e 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
@@ -17,8 +17,19 @@
 #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>
@@ -29,134 +40,146 @@
 #include <utility>
 #include <vector>
 
-#include "ExecutionBurstUtils.h"
-#include "HalInterfaces.h"
 #include "Tracing.h"
 
-namespace android::nn {
+namespace android::hardware::neuralnetworks::V1_2::utils {
 namespace {
 
-// DefaultBurstExecutorWithCache adapts an IPreparedModel so that it can be
-// used as an IBurstExecutorWithCache. Specifically, the cache simply stores the
-// hidl_memory object, and the execution forwards calls to the provided
-// IPreparedModel's "executeSynchronously" method. With this class, hidl_memory
-// must be mapped and unmapped for each execution.
-class DefaultBurstExecutorWithCache : public ExecutionBurstServer::IBurstExecutorWithCache {
-  public:
-    DefaultBurstExecutorWithCache(V1_2::IPreparedModel* preparedModel)
-        : mpPreparedModel(preparedModel) {}
+using neuralnetworks::utils::makeExecutionFailure;
 
-    bool isCacheEntryPresent(int32_t slot) const override {
-        const auto it = mMemoryCache.find(slot);
-        return (it != mMemoryCache.end()) && it->second.valid();
+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)));
     }
-
-    void addCacheEntry(const hardware::hidl_memory& memory, int32_t slot) override {
-        mMemoryCache[slot] = memory;
-    }
-
-    void removeCacheEntry(int32_t slot) override { mMemoryCache.erase(slot); }
-
-    std::tuple<V1_0::ErrorStatus, hardware::hidl_vec<V1_2::OutputShape>, V1_2::Timing> execute(
-            const V1_0::Request& request, const std::vector<int32_t>& slots,
-            V1_2::MeasureTiming measure) override {
-        // convert slots to pools
-        hardware::hidl_vec<hardware::hidl_memory> pools(slots.size());
-        std::transform(slots.begin(), slots.end(), pools.begin(),
-                       [this](int32_t slot) { return mMemoryCache[slot]; });
-
-        // create full request
-        V1_0::Request fullRequest = request;
-        fullRequest.pools = std::move(pools);
-
-        // setup execution
-        V1_0::ErrorStatus returnedStatus = V1_0::ErrorStatus::GENERAL_FAILURE;
-        hardware::hidl_vec<V1_2::OutputShape> returnedOutputShapes;
-        V1_2::Timing returnedTiming;
-        auto cb = [&returnedStatus, &returnedOutputShapes, &returnedTiming](
-                          V1_0::ErrorStatus status,
-                          const hardware::hidl_vec<V1_2::OutputShape>& outputShapes,
-                          const V1_2::Timing& timing) {
-            returnedStatus = status;
-            returnedOutputShapes = outputShapes;
-            returnedTiming = timing;
-        };
-
-        // execute
-        const hardware::Return<void> ret =
-                mpPreparedModel->executeSynchronously(fullRequest, measure, cb);
-        if (!ret.isOk() || returnedStatus != V1_0::ErrorStatus::NONE) {
-            LOG(ERROR) << "IPreparedModelAdapter::execute -- Error executing";
-            return {returnedStatus, std::move(returnedOutputShapes), kNoTiming};
-        }
-
-        return std::make_tuple(returnedStatus, std::move(returnedOutputShapes), returnedTiming);
-    }
-
-  private:
-    V1_2::IPreparedModel* const mpPreparedModel;
-    std::map<int32_t, hardware::hidl_memory> mMemoryCache;
-};
+    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
 
-sp<ExecutionBurstServer> ExecutionBurstServer::create(
+nn::GeneralResult<sp<ExecutionBurstServer>> ExecutionBurstServer::create(
         const sp<IBurstCallback>& callback, const MQDescriptorSync<FmqRequestDatum>& requestChannel,
-        const MQDescriptorSync<FmqResultDatum>& resultChannel,
-        std::shared_ptr<IBurstExecutorWithCache> executorWithCache,
+        const MQDescriptorSync<FmqResultDatum>& resultChannel, nn::SharedBurst burstExecutor,
         std::chrono::microseconds pollingTimeWindow) {
     // check inputs
-    if (callback == nullptr || executorWithCache == nullptr) {
-        LOG(ERROR) << "ExecutionBurstServer::create passed a nullptr";
-        return nullptr;
+    if (callback == nullptr || burstExecutor == nullptr) {
+        return NN_ERROR() << "ExecutionBurstServer::create passed a nullptr";
     }
 
     // create FMQ objects
-    std::unique_ptr<RequestChannelReceiver> requestChannelReceiver =
-            RequestChannelReceiver::create(requestChannel, pollingTimeWindow);
-    std::unique_ptr<ResultChannelSender> resultChannelSender =
-            ResultChannelSender::create(resultChannel);
+    auto requestChannelReceiver =
+            NN_TRY(RequestChannelReceiver::create(requestChannel, pollingTimeWindow));
+    auto resultChannelSender = NN_TRY(ResultChannelSender::create(resultChannel));
 
     // check FMQ objects
-    if (!requestChannelReceiver || !resultChannelSender) {
-        LOG(ERROR) << "ExecutionBurstServer::create failed to create FastMessageQueue";
-        return nullptr;
-    }
+    CHECK(requestChannelReceiver != nullptr);
+    CHECK(resultChannelSender != nullptr);
 
     // make and return context
-    return new ExecutionBurstServer(callback, std::move(requestChannelReceiver),
-                                    std::move(resultChannelSender), std::move(executorWithCache));
+    return sp<ExecutionBurstServer>::make(PrivateConstructorTag{}, callback,
+                                          std::move(requestChannelReceiver),
+                                          std::move(resultChannelSender), std::move(burstExecutor));
 }
 
-sp<ExecutionBurstServer> ExecutionBurstServer::create(
-        const sp<IBurstCallback>& callback, const MQDescriptorSync<FmqRequestDatum>& requestChannel,
-        const MQDescriptorSync<FmqResultDatum>& resultChannel, V1_2::IPreparedModel* preparedModel,
-        std::chrono::microseconds pollingTimeWindow) {
-    // check relevant input
-    if (preparedModel == nullptr) {
-        LOG(ERROR) << "ExecutionBurstServer::create passed a nullptr";
-        return nullptr;
-    }
-
-    // adapt IPreparedModel to have caching
-    const std::shared_ptr<DefaultBurstExecutorWithCache> preparedModelAdapter =
-            std::make_shared<DefaultBurstExecutorWithCache>(preparedModel);
-
-    // make and return context
-    return ExecutionBurstServer::create(callback, requestChannel, resultChannel,
-                                        preparedModelAdapter, pollingTimeWindow);
-}
-
-ExecutionBurstServer::ExecutionBurstServer(
-        const sp<IBurstCallback>& callback, std::unique_ptr<RequestChannelReceiver> requestChannel,
-        std::unique_ptr<ResultChannelSender> resultChannel,
-        std::shared_ptr<IBurstExecutorWithCache> executorWithCache)
+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)),
-      mExecutorWithCache(std::move(executorWithCache)) {
+      mBurstExecutor(std::move(burstExecutor)),
+      mMemoryCache(mBurstExecutor, mCallback) {
     // TODO: highly document the threading behavior of this class
     mWorker = std::thread([this] { task(); });
 }
@@ -170,51 +193,9 @@
     mWorker.join();
 }
 
-hardware::Return<void> ExecutionBurstServer::freeMemory(int32_t slot) {
-    std::lock_guard<std::mutex> hold(mMutex);
-    mExecutorWithCache->removeCacheEntry(slot);
-    return hardware::Void();
-}
-
-void ExecutionBurstServer::ensureCacheEntriesArePresentLocked(const std::vector<int32_t>& slots) {
-    const auto slotIsKnown = [this](int32_t slot) {
-        return mExecutorWithCache->isCacheEntryPresent(slot);
-    };
-
-    // find unique unknown slots
-    std::vector<int32_t> unknownSlots = slots;
-    auto unknownSlotsEnd = unknownSlots.end();
-    std::sort(unknownSlots.begin(), unknownSlotsEnd);
-    unknownSlotsEnd = std::unique(unknownSlots.begin(), unknownSlotsEnd);
-    unknownSlotsEnd = std::remove_if(unknownSlots.begin(), unknownSlotsEnd, slotIsKnown);
-    unknownSlots.erase(unknownSlotsEnd, unknownSlots.end());
-
-    // quick-exit if all slots are known
-    if (unknownSlots.empty()) {
-        return;
-    }
-
-    V1_0::ErrorStatus errorStatus = V1_0::ErrorStatus::GENERAL_FAILURE;
-    std::vector<hardware::hidl_memory> returnedMemories;
-    auto cb = [&errorStatus, &returnedMemories](
-                      V1_0::ErrorStatus status,
-                      const hardware::hidl_vec<hardware::hidl_memory>& memories) {
-        errorStatus = status;
-        returnedMemories = memories;
-    };
-
-    const hardware::Return<void> ret = mCallback->getMemories(unknownSlots, cb);
-
-    if (!ret.isOk() || errorStatus != V1_0::ErrorStatus::NONE ||
-        returnedMemories.size() != unknownSlots.size()) {
-        LOG(ERROR) << "Error retrieving memories";
-        return;
-    }
-
-    // add memories to unknown slots
-    for (size_t i = 0; i < unknownSlots.size(); ++i) {
-        mExecutorWithCache->addCacheEntry(returnedMemories[i], unknownSlots[i]);
-    }
+Return<void> ExecutionBurstServer::freeMemory(int32_t slot) {
+    mMemoryCache.removeCacheEntry(slot);
+    return Void();
 }
 
 void ExecutionBurstServer::task() {
@@ -223,38 +204,65 @@
         // receive request
         auto arguments = mRequestChannelReceiver->getBlocking();
 
-        // if the request packet was not properly received, return a generic
-        // error and skip the execution
+        // 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) {
+        // 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;
         }
 
-        // otherwise begin tracing execution
-        NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
-                     "ExecutionBurstServer getting memory, executing, and returning results");
+        // unpack the arguments; types are Request, std::vector<int32_t>, and MeasureTiming,
+        // respectively
+        const auto [requestWithoutPools, slotsOfPools, measure] = std::move(arguments).value();
 
-        // unpack the arguments; types are Request, std::vector<int32_t>, and
-        // MeasureTiming, respectively
-        const auto [requestWithoutPools, slotsOfPools, measure] = std::move(*arguments);
-
-        // ensure executor with cache has required memory
-        std::lock_guard<std::mutex> hold(mMutex);
-        ensureCacheEntriesArePresentLocked(slotsOfPools);
-
-        // perform computation; types are ErrorStatus, hidl_vec<OutputShape>,
-        // and Timing, respectively
-        const auto [errorStatus, outputShapes, returnedTiming] =
-                mExecutorWithCache->execute(requestWithoutPools, slotsOfPools, measure);
+        auto result = execute(requestWithoutPools, slotsOfPools, measure);
 
         // return result
-        mResultChannelSender->send(errorStatus, outputShapes, returnedTiming);
+        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);
+        }
     }
 }
 
-}  // namespace android::nn
+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
index f0275f9..1bdde1e 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
@@ -19,11 +19,15 @@
 #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>
@@ -39,84 +43,97 @@
 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() + request.pools.size();
+    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
-    {
-        FmqRequestDatum datum;
-        datum.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>(request.pools.size())});
-        data.push_back(datum);
-    }
+    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
-        FmqRequestDatum datum;
-        datum.inputOperandInformation(
-                {/*.hasNoValue=*/input.hasNoValue,
-                 /*.location=*/input.location,
-                 /*.numberOfDimensions=*/static_cast<uint32_t>(input.dimensions.size())});
-        data.push_back(datum);
+        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) {
-            FmqRequestDatum datum;
-            datum.inputOperandDimensionValue(dimension);
-            data.push_back(datum);
+            data.emplace_back();
+            data.back().inputOperandDimensionValue(dimension);
         }
     }
 
     // package output data
     for (const auto& output : request.outputs) {
         // package operand information
-        FmqRequestDatum datum;
-        datum.outputOperandInformation(
-                {/*.hasNoValue=*/output.hasNoValue,
-                 /*.location=*/output.location,
-                 /*.numberOfDimensions=*/static_cast<uint32_t>(output.dimensions.size())});
-        data.push_back(datum);
+        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) {
-            FmqRequestDatum datum;
-            datum.outputOperandDimensionValue(dimension);
-            data.push_back(datum);
+            data.emplace_back();
+            data.back().outputOperandDimensionValue(dimension);
         }
     }
 
     // package pool identifier
     for (int32_t slot : slots) {
-        FmqRequestDatum datum;
-        datum.poolIdentifier(slot);
-        data.push_back(datum);
+        data.emplace_back();
+        data.back().poolIdentifier(slot);
     }
 
     // package measureTiming
-    {
-        FmqRequestDatum datum;
-        datum.measureTiming(measure);
-        data.push_back(datum);
-    }
+    data.emplace_back();
+    data.back().measureTiming(measure);
+
+    CHECK_EQ(data.size(), count);
 
     // return packet
     return data;
@@ -137,46 +154,38 @@
     data.reserve(count);
 
     // package packetInfo
-    {
-        FmqResultDatum datum;
-        datum.packetInformation({/*.packetSize=*/static_cast<uint32_t>(count),
-                                 /*.errorStatus=*/errorStatus,
-                                 /*.numberOfOperands=*/static_cast<uint32_t>(outputShapes.size())});
-        data.push_back(datum);
-    }
+    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
-        FmqResultDatum::OperandInformation info{};
-        info.isSufficient = operand.isSufficient;
-        info.numberOfDimensions = static_cast<uint32_t>(operand.dimensions.size());
-
-        FmqResultDatum datum;
-        datum.operandInformation(info);
-        data.push_back(datum);
+        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) {
-            FmqResultDatum datum;
-            datum.operandDimensionValue(dimension);
-            data.push_back(datum);
+            data.emplace_back();
+            data.back().operandDimensionValue(dimension);
         }
     }
 
     // package executionTiming
-    {
-        FmqResultDatum datum;
-        datum.executionTiming(timing);
-        data.push_back(datum);
-    }
+    data.emplace_back();
+    data.back().executionTiming(timing);
+
+    CHECK_EQ(data.size(), count);
 
     // return result
     return data;
 }
 
 // deserialize request
-std::optional<std::tuple<V1_0::Request, std::vector<int32_t>, V1_2::MeasureTiming>> deserialize(
+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;
 
@@ -184,8 +193,7 @@
 
     // validate packet information
     if (data.size() == 0 || data[index].getDiscriminator() != discriminator::packetInformation) {
-        LOG(ERROR) << "FMQ Request packet ill-formed";
-        return std::nullopt;
+        return NN_ERROR() << "FMQ Request packet ill-formed";
     }
 
     // unpackage packet information
@@ -198,8 +206,7 @@
 
     // verify packet size
     if (data.size() != packetSize) {
-        LOG(ERROR) << "FMQ Request packet ill-formed";
-        return std::nullopt;
+        return NN_ERROR() << "FMQ Request packet ill-formed";
     }
 
     // unpackage input operands
@@ -208,8 +215,7 @@
     for (size_t operand = 0; operand < numberOfInputOperands; ++operand) {
         // validate input operand information
         if (data[index].getDiscriminator() != discriminator::inputOperandInformation) {
-            LOG(ERROR) << "FMQ Request packet ill-formed";
-            return std::nullopt;
+            return NN_ERROR() << "FMQ Request packet ill-formed";
         }
 
         // unpackage operand information
@@ -226,8 +232,7 @@
         for (size_t i = 0; i < numberOfDimensions; ++i) {
             // validate dimension
             if (data[index].getDiscriminator() != discriminator::inputOperandDimensionValue) {
-                LOG(ERROR) << "FMQ Request packet ill-formed";
-                return std::nullopt;
+                return NN_ERROR() << "FMQ Request packet ill-formed";
             }
 
             // unpackage dimension
@@ -240,7 +245,7 @@
 
         // store result
         inputs.push_back(
-                {/*.hasNoValue=*/hasNoValue, /*.location=*/location, /*.dimensions=*/dimensions});
+                {.hasNoValue = hasNoValue, .location = location, .dimensions = dimensions});
     }
 
     // unpackage output operands
@@ -249,8 +254,7 @@
     for (size_t operand = 0; operand < numberOfOutputOperands; ++operand) {
         // validate output operand information
         if (data[index].getDiscriminator() != discriminator::outputOperandInformation) {
-            LOG(ERROR) << "FMQ Request packet ill-formed";
-            return std::nullopt;
+            return NN_ERROR() << "FMQ Request packet ill-formed";
         }
 
         // unpackage operand information
@@ -267,8 +271,7 @@
         for (size_t i = 0; i < numberOfDimensions; ++i) {
             // validate dimension
             if (data[index].getDiscriminator() != discriminator::outputOperandDimensionValue) {
-                LOG(ERROR) << "FMQ Request packet ill-formed";
-                return std::nullopt;
+                return NN_ERROR() << "FMQ Request packet ill-formed";
             }
 
             // unpackage dimension
@@ -281,7 +284,7 @@
 
         // store result
         outputs.push_back(
-                {/*.hasNoValue=*/hasNoValue, /*.location=*/location, /*.dimensions=*/dimensions});
+                {.hasNoValue = hasNoValue, .location = location, .dimensions = dimensions});
     }
 
     // unpackage pools
@@ -290,8 +293,7 @@
     for (size_t pool = 0; pool < numberOfPools; ++pool) {
         // validate input operand information
         if (data[index].getDiscriminator() != discriminator::poolIdentifier) {
-            LOG(ERROR) << "FMQ Request packet ill-formed";
-            return std::nullopt;
+            return NN_ERROR() << "FMQ Request packet ill-formed";
         }
 
         // unpackage operand information
@@ -304,8 +306,7 @@
 
     // validate measureTiming
     if (data[index].getDiscriminator() != discriminator::measureTiming) {
-        LOG(ERROR) << "FMQ Request packet ill-formed";
-        return std::nullopt;
+        return NN_ERROR() << "FMQ Request packet ill-formed";
     }
 
     // unpackage measureTiming
@@ -314,27 +315,23 @@
 
     // validate packet information
     if (index != packetSize) {
-        LOG(ERROR) << "FMQ Result packet ill-formed";
-        return std::nullopt;
+        return NN_ERROR() << "FMQ Result packet ill-formed";
     }
 
     // return request
-    V1_0::Request request = {/*.inputs=*/inputs, /*.outputs=*/outputs, /*.pools=*/{}};
+    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
-std::optional<std::tuple<V1_0::ErrorStatus, std::vector<V1_2::OutputShape>, V1_2::Timing>>
-deserialize(const std::vector<FmqResultDatum>& data) {
+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;
-
-    std::vector<V1_2::OutputShape> outputShapes;
     size_t index = 0;
 
     // validate packet information
     if (data.size() == 0 || data[index].getDiscriminator() != discriminator::packetInformation) {
-        LOG(ERROR) << "FMQ Result packet ill-formed";
-        return std::nullopt;
+        return NN_ERROR() << "FMQ Result packet ill-formed";
     }
 
     // unpackage packet information
@@ -346,16 +343,16 @@
 
     // verify packet size
     if (data.size() != packetSize) {
-        LOG(ERROR) << "FMQ Result packet ill-formed";
-        return std::nullopt;
+        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) {
-            LOG(ERROR) << "FMQ Result packet ill-formed";
-            return std::nullopt;
+            return NN_ERROR() << "FMQ Result packet ill-formed";
         }
 
         // unpackage operand information
@@ -370,8 +367,7 @@
         for (size_t i = 0; i < numberOfDimensions; ++i) {
             // validate dimension
             if (data[index].getDiscriminator() != discriminator::operandDimensionValue) {
-                LOG(ERROR) << "FMQ Result packet ill-formed";
-                return std::nullopt;
+                return NN_ERROR() << "FMQ Result packet ill-formed";
             }
 
             // unpackage dimension
@@ -383,13 +379,12 @@
         }
 
         // store result
-        outputShapes.push_back({/*.dimensions=*/dimensions, /*.isSufficient=*/isSufficient});
+        outputShapes.push_back({.dimensions = dimensions, .isSufficient = isSufficient});
     }
 
     // validate execution timing
     if (data[index].getDiscriminator() != discriminator::executionTiming) {
-        LOG(ERROR) << "FMQ Result packet ill-formed";
-        return std::nullopt;
+        return NN_ERROR() << "FMQ Result packet ill-formed";
     }
 
     // unpackage execution timing
@@ -398,123 +393,113 @@
 
     // validate packet information
     if (index != packetSize) {
-        LOG(ERROR) << "FMQ Result packet ill-formed";
-        return std::nullopt;
+        return NN_ERROR() << "FMQ Result packet ill-formed";
     }
 
     // return result
     return std::make_tuple(errorStatus, std::move(outputShapes), timing);
 }
 
-V1_0::ErrorStatus legacyConvertResultCodeToErrorStatus(int resultCode) {
-    return convertToV1_0(convertResultCodeToErrorStatus(resultCode));
-}
-
 // RequestChannelSender methods
 
-std::pair<std::unique_ptr<RequestChannelSender>, const FmqRequestDescriptor*>
+nn::GeneralResult<
+        std::pair<std::unique_ptr<RequestChannelSender>, const MQDescriptorSync<FmqRequestDatum>*>>
 RequestChannelSender::create(size_t channelLength) {
-    std::unique_ptr<FmqRequestChannel> fmqRequestChannel =
-            std::make_unique<FmqRequestChannel>(channelLength, /*confEventFlag=*/true);
-    if (!fmqRequestChannel->isValid()) {
-        LOG(ERROR) << "Unable to create RequestChannelSender";
-        return {nullptr, nullptr};
+    auto requestChannelSender =
+            std::make_unique<RequestChannelSender>(PrivateConstructorTag{}, channelLength);
+    if (!requestChannelSender->mFmqRequestChannel.isValid()) {
+        return NN_ERROR() << "Unable to create RequestChannelSender";
     }
 
-    const FmqRequestDescriptor* descriptor = fmqRequestChannel->getDesc();
-    return std::make_pair(std::make_unique<RequestChannelSender>(std::move(fmqRequestChannel)),
-                          descriptor);
+    const MQDescriptorSync<FmqRequestDatum>* descriptor =
+            requestChannelSender->mFmqRequestChannel.getDesc();
+    return std::make_pair(std::move(requestChannelSender), descriptor);
 }
 
-RequestChannelSender::RequestChannelSender(std::unique_ptr<FmqRequestChannel> fmqRequestChannel)
-    : mFmqRequestChannel(std::move(fmqRequestChannel)) {}
+RequestChannelSender::RequestChannelSender(PrivateConstructorTag /*tag*/, size_t channelLength)
+    : mFmqRequestChannel(channelLength, /*configureEventFlagWord=*/true) {}
 
-bool RequestChannelSender::send(const V1_0::Request& request, V1_2::MeasureTiming measure,
-                                const std::vector<int32_t>& slots) {
+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);
 }
 
-bool RequestChannelSender::sendPacket(const std::vector<FmqRequestDatum>& packet) {
+nn::Result<void> RequestChannelSender::sendPacket(const std::vector<FmqRequestDatum>& packet) {
     if (!mValid) {
-        return false;
+        return NN_ERROR() << "FMQ object is invalid";
     }
 
-    if (packet.size() > mFmqRequestChannel->availableToWrite()) {
-        LOG(ERROR)
-                << "RequestChannelSender::sendPacket -- packet size exceeds size available in FMQ";
-        return false;
+    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.
-    return mFmqRequestChannel->writeBlocking(packet.data(), packet.size());
+    // 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::invalidate() {
+void RequestChannelSender::notifyAsDeadObject() {
     mValid = false;
 }
 
 // RequestChannelReceiver methods
 
-std::unique_ptr<RequestChannelReceiver> RequestChannelReceiver::create(
-        const FmqRequestDescriptor& requestChannel, std::chrono::microseconds pollingTimeWindow) {
-    std::unique_ptr<FmqRequestChannel> fmqRequestChannel =
-            std::make_unique<FmqRequestChannel>(requestChannel);
+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 (!fmqRequestChannel->isValid()) {
-        LOG(ERROR) << "Unable to create RequestChannelReceiver";
-        return nullptr;
+    if (!requestChannelReceiver->mFmqRequestChannel.isValid()) {
+        return NN_ERROR() << "Unable to create RequestChannelReceiver";
     }
-    if (fmqRequestChannel->getEventFlagWord() == nullptr) {
-        LOG(ERROR)
-                << "RequestChannelReceiver::create was passed an MQDescriptor without an EventFlag";
-        return nullptr;
+    if (requestChannelReceiver->mFmqRequestChannel.getEventFlagWord() == nullptr) {
+        return NN_ERROR()
+               << "RequestChannelReceiver::create was passed an MQDescriptor without an EventFlag";
     }
 
-    return std::make_unique<RequestChannelReceiver>(std::move(fmqRequestChannel),
-                                                    pollingTimeWindow);
+    return requestChannelReceiver;
 }
 
-RequestChannelReceiver::RequestChannelReceiver(std::unique_ptr<FmqRequestChannel> fmqRequestChannel,
-                                               std::chrono::microseconds pollingTimeWindow)
-    : mFmqRequestChannel(std::move(fmqRequestChannel)), kPollingTimeWindow(pollingTimeWindow) {}
+RequestChannelReceiver::RequestChannelReceiver(
+        PrivateConstructorTag /*tag*/, const MQDescriptorSync<FmqRequestDatum>& requestChannel,
+        std::chrono::microseconds pollingTimeWindow)
+    : mFmqRequestChannel(requestChannel), kPollingTimeWindow(pollingTimeWindow) {}
 
-std::optional<std::tuple<V1_0::Request, std::vector<int32_t>, V1_2::MeasureTiming>>
+nn::Result<std::tuple<V1_0::Request, std::vector<int32_t>, V1_2::MeasureTiming>>
 RequestChannelReceiver::getBlocking() {
-    const auto packet = getPacketBlocking();
-    if (!packet) {
-        return std::nullopt;
-    }
-
-    return deserialize(*packet);
+    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.
-    // TODO: look for a different/better way to signal/notify the futex to wake
-    // up any thread waiting on it
-    FmqRequestDatum datum;
-    datum.packetInformation({/*.packetSize=*/0, /*.numberOfInputOperands=*/0,
-                             /*.numberOfOutputOperands=*/0, /*.numberOfPools=*/0});
-    mFmqRequestChannel->writeBlocking(&datum, 1);
+    // 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());
 }
 
-std::optional<std::vector<FmqRequestDatum>> RequestChannelReceiver::getPacketBlocking() {
+nn::Result<std::vector<FmqRequestDatum>> RequestChannelReceiver::getPacketBlocking() {
     if (mTeardown) {
-        return std::nullopt;
+        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.
+    // 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;
@@ -522,173 +507,146 @@
     while (getCurrentTime() < timeToStopPolling) {
         // if class is being torn down, immediately return
         if (mTeardown.load(std::memory_order_relaxed)) {
-            return std::nullopt;
+            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();
+        // Check if data is available. If it is, immediately retrieve it and return.
+        const size_t available = mFmqRequestChannel.availableToRead();
         if (available > 0) {
-            // This is the first point when we know an execution is occurring,
-            // so begin to collect systraces. Note that a similar systrace does
-            // not exist at the corresponding point in
-            // ResultChannelReceiver::getPacketBlocking because the execution is
-            // already in flight.
-            NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
-                         "ExecutionBurstServer getting packet");
             std::vector<FmqRequestDatum> packet(available);
-            const bool success = mFmqRequestChannel->read(packet.data(), available);
+            const bool success = mFmqRequestChannel.readBlocking(packet.data(), available);
             if (!success) {
-                LOG(ERROR) << "Error receiving packet";
-                return std::nullopt;
+                return NN_ERROR() << "Error receiving packet";
             }
-            return std::make_optional(std::move(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.
+    // 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);
-
-    // This is the first point when we know an execution is occurring, so begin
-    // to collect systraces. Note that a similar systrace does not exist at the
-    // corresponding point in ResultChannelReceiver::getPacketBlocking because
-    // the execution is already in flight.
-    NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION, "ExecutionBurstServer getting packet");
+    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();
+    // 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);
+    success &= mFmqRequestChannel.read(packet.data() + 1, count);
 
     // terminate loop
     if (mTeardown) {
-        return std::nullopt;
+        return NN_ERROR() << "FMQ object is being torn down";
     }
 
     // ensure packet was successfully received
     if (!success) {
-        LOG(ERROR) << "Error receiving packet";
-        return std::nullopt;
+        return NN_ERROR() << "Error receiving packet";
     }
 
-    return std::make_optional(std::move(packet));
+    return packet;
 }
 
 // ResultChannelSender methods
 
-std::unique_ptr<ResultChannelSender> ResultChannelSender::create(
-        const FmqResultDescriptor& resultChannel) {
-    std::unique_ptr<FmqResultChannel> fmqResultChannel =
-            std::make_unique<FmqResultChannel>(resultChannel);
+nn::GeneralResult<std::unique_ptr<ResultChannelSender>> ResultChannelSender::create(
+        const MQDescriptorSync<FmqResultDatum>& resultChannel) {
+    auto resultChannelSender =
+            std::make_unique<ResultChannelSender>(PrivateConstructorTag{}, resultChannel);
 
-    if (!fmqResultChannel->isValid()) {
-        LOG(ERROR) << "Unable to create RequestChannelSender";
-        return nullptr;
+    if (!resultChannelSender->mFmqResultChannel.isValid()) {
+        return NN_ERROR() << "Unable to create RequestChannelSender";
     }
-    if (fmqResultChannel->getEventFlagWord() == nullptr) {
-        LOG(ERROR) << "ResultChannelSender::create was passed an MQDescriptor without an EventFlag";
-        return nullptr;
+    if (resultChannelSender->mFmqResultChannel.getEventFlagWord() == nullptr) {
+        return NN_ERROR()
+               << "ResultChannelSender::create was passed an MQDescriptor without an EventFlag";
     }
 
-    return std::make_unique<ResultChannelSender>(std::move(fmqResultChannel));
+    return resultChannelSender;
 }
 
-ResultChannelSender::ResultChannelSender(std::unique_ptr<FmqResultChannel> fmqResultChannel)
-    : mFmqResultChannel(std::move(fmqResultChannel)) {}
+ResultChannelSender::ResultChannelSender(PrivateConstructorTag /*tag*/,
+                                         const MQDescriptorSync<FmqResultDatum>& resultChannel)
+    : mFmqResultChannel(resultChannel) {}
 
-bool ResultChannelSender::send(V1_0::ErrorStatus errorStatus,
+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);
-    return sendPacket(serialized);
+    sendPacket(serialized);
 }
 
-bool ResultChannelSender::sendPacket(const std::vector<FmqResultDatum>& packet) {
-    if (packet.size() > mFmqResultChannel->availableToWrite()) {
+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.
-        return mFmqResultChannel->writeBlocking(errorPacket.data(), errorPacket.size());
+        // 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());
     }
-
-    // Always send the packet with "blocking" because this signals the futex and
-    // unblocks the consumer if it is waiting on the futex.
-    return mFmqResultChannel->writeBlocking(packet.data(), packet.size());
 }
 
 // ResultChannelReceiver methods
 
-std::pair<std::unique_ptr<ResultChannelReceiver>, const FmqResultDescriptor*>
+nn::GeneralResult<
+        std::pair<std::unique_ptr<ResultChannelReceiver>, const MQDescriptorSync<FmqResultDatum>*>>
 ResultChannelReceiver::create(size_t channelLength, std::chrono::microseconds pollingTimeWindow) {
-    std::unique_ptr<FmqResultChannel> fmqResultChannel =
-            std::make_unique<FmqResultChannel>(channelLength, /*confEventFlag=*/true);
-    if (!fmqResultChannel->isValid()) {
-        LOG(ERROR) << "Unable to create ResultChannelReceiver";
-        return {nullptr, nullptr};
+    auto resultChannelReceiver = std::make_unique<ResultChannelReceiver>(
+            PrivateConstructorTag{}, channelLength, pollingTimeWindow);
+    if (!resultChannelReceiver->mFmqResultChannel.isValid()) {
+        return NN_ERROR() << "Unable to create ResultChannelReceiver";
     }
 
-    const FmqResultDescriptor* descriptor = fmqResultChannel->getDesc();
-    return std::make_pair(
-            std::make_unique<ResultChannelReceiver>(std::move(fmqResultChannel), pollingTimeWindow),
-            descriptor);
+    const MQDescriptorSync<FmqResultDatum>* descriptor =
+            resultChannelReceiver->mFmqResultChannel.getDesc();
+    return std::make_pair(std::move(resultChannelReceiver), descriptor);
 }
 
-ResultChannelReceiver::ResultChannelReceiver(std::unique_ptr<FmqResultChannel> fmqResultChannel,
+ResultChannelReceiver::ResultChannelReceiver(PrivateConstructorTag /*tag*/, size_t channelLength,
                                              std::chrono::microseconds pollingTimeWindow)
-    : mFmqResultChannel(std::move(fmqResultChannel)), kPollingTimeWindow(pollingTimeWindow) {}
+    : mFmqResultChannel(channelLength, /*configureEventFlagWord=*/true),
+      kPollingTimeWindow(pollingTimeWindow) {}
 
-std::optional<std::tuple<V1_0::ErrorStatus, std::vector<V1_2::OutputShape>, V1_2::Timing>>
+nn::Result<std::tuple<V1_0::ErrorStatus, std::vector<V1_2::OutputShape>, V1_2::Timing>>
 ResultChannelReceiver::getBlocking() {
-    const auto packet = getPacketBlocking();
-    if (!packet) {
-        return std::nullopt;
-    }
-
-    return deserialize(*packet);
+    const auto packet = NN_TRY(getPacketBlocking());
+    return deserialize(packet);
 }
 
-void ResultChannelReceiver::invalidate() {
+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.
-    // TODO: look for a different/better way to signal/notify the futex to
-    // wake up any thread waiting on it
-    FmqResultDatum datum;
-    datum.packetInformation({/*.packetSize=*/0,
-                             /*.errorStatus=*/V1_0::ErrorStatus::GENERAL_FAILURE,
-                             /*.numberOfOperands=*/0});
-    mFmqResultChannel->writeBlocking(&datum, 1);
+    // 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());
 }
 
-std::optional<std::vector<FmqResultDatum>> ResultChannelReceiver::getPacketBlocking() {
+nn::Result<std::vector<FmqResultDatum>> ResultChannelReceiver::getPacketBlocking() {
     if (!mValid) {
-        return std::nullopt;
+        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.
+    // 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;
@@ -696,54 +654,51 @@
     while (getCurrentTime() < timeToStopPolling) {
         // if class is being torn down, immediately return
         if (!mValid.load(std::memory_order_relaxed)) {
-            return std::nullopt;
+            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();
+        // 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->read(packet.data(), available);
+            const bool success = mFmqResultChannel.readBlocking(packet.data(), available);
             if (!success) {
-                LOG(ERROR) << "Error receiving packet";
-                return std::nullopt;
+                return NN_ERROR() << "Error receiving packet";
             }
-            return std::make_optional(std::move(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.
+    // 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);
+    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();
+    // 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);
+    success &= mFmqResultChannel.read(packet.data() + 1, count);
 
     if (!mValid) {
-        return std::nullopt;
+        return NN_ERROR() << "FMQ object is invalid";
     }
 
     // ensure packet was successfully received
     if (!success) {
-        LOG(ERROR) << "Error receiving packet";
-        return std::nullopt;
+        return NN_ERROR() << "Error receiving packet";
     }
 
-    return std::make_optional(std::move(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
index 6841c5e..b209a44 100644
--- a/neuralnetworks/1.2/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
@@ -18,6 +18,8 @@
 
 #include "Callbacks.h"
 #include "Conversions.h"
+#include "ExecutionBurstController.h"
+#include "ExecutionBurstUtils.h"
 #include "Utils.h"
 
 #include <android/hardware/neuralnetworks/1.0/types.h>
@@ -27,12 +29,12 @@
 #include <nnapi/IPreparedModel.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
-#include <nnapi/hal/1.0/Burst.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>
@@ -119,7 +121,16 @@
 }
 
 nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
-    return V1_0::utils::Burst::create(shared_from_this());
+    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(kPreparedModel, std::move(fallback), pollingTimeWindow);
 }
 
 std::any PreparedModel::getUnderlyingResource() const {
diff --git a/neuralnetworks/1.2/utils/test/DeviceTest.cpp b/neuralnetworks/1.2/utils/test/DeviceTest.cpp
index 9c8adde..215d44c 100644
--- a/neuralnetworks/1.2/utils/test/DeviceTest.cpp
+++ b/neuralnetworks/1.2/utils/test/DeviceTest.cpp
@@ -772,7 +772,7 @@
     EXPECT_NE(result.value(), nullptr);
 }
 
-TEST(DeviceTest, prepareModelFromCacheError) {
+TEST(DeviceTest, prepareModelFromCacheLaunchError) {
     // setup call
     const auto mockDevice = createMockDevice();
     const auto device = Device::create(kName, mockDevice).value();
@@ -790,6 +790,23 @@
     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();
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
index b459943..0d34c70 100644
--- a/neuralnetworks/1.2/utils/test/MockDevice.h
+++ b/neuralnetworks/1.2/utils/test/MockDevice.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE
+#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>
@@ -114,4 +114,4 @@
 
 }  // namespace android::hardware::neuralnetworks::V1_2::utils
 
-#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE
+#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
index f5fd1f3..bd81712 100644
--- a/neuralnetworks/1.2/utils/test/MockPreparedModel.h
+++ b/neuralnetworks/1.2/utils/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL
+#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>
@@ -98,4 +98,4 @@
 
 }  // namespace android::hardware::neuralnetworks::V1_2::utils
 
-#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL
+#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
index 5062ac9..d297b1a 100644
--- a/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
@@ -16,6 +16,8 @@
 
 #include "MockPreparedModel.h"
 
+#include "MockBurstContext.h"
+
 #include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -67,6 +69,17 @@
         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); };
@@ -321,7 +334,76 @@
     EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
 }
 
-// TODO: test burst execution if/when it is added to nn::IPreparedModel.
+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
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.3/Android.bp b/neuralnetworks/1.3/Android.bp
index 3e02c90..aa8fc39 100644
--- a/neuralnetworks/1.3/Android.bp
+++ b/neuralnetworks/1.3/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.neuralnetworks@1.3",
     root: "android.hardware",
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 5f5ee03..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,
@@ -5334,7 +5340,6 @@
     HIGH,
 };
 
-
 /**
  * The capabilities of a driver.
  *
diff --git a/neuralnetworks/1.3/types.t b/neuralnetworks/1.3/types.t
index 2901d18..96d1a1b 100644
--- a/neuralnetworks/1.3/types.t
+++ b/neuralnetworks/1.3/types.t
@@ -90,460 +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.
-     *
-     * 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}.
-     */
-    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.main.inputIndexes.
-     *   E.g., input[i] corresponds to Model.main.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.main.outputIndexes.
-     *   E.g., output[i] corresponds to Model.main.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
index 41d9521..28c036a 100644
--- a/neuralnetworks/1.3/utils/Android.bp
+++ b/neuralnetworks/1.3/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: "neuralnetworks_utils_hal_1_3",
     defaults: ["neuralnetworks_utils_defaults"],
@@ -33,6 +42,7 @@
         "android.hardware.neuralnetworks@1.1",
         "android.hardware.neuralnetworks@1.2",
         "android.hardware.neuralnetworks@1.3",
+        "libfmq",
     ],
     export_static_lib_headers: [
         "neuralnetworks_utils_hal_common",
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
index fda79c8..69e87f7 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h
@@ -42,8 +42,8 @@
 
     nn::Request::MemoryDomainToken getToken() const override;
 
-    nn::GeneralResult<void> copyTo(const nn::Memory& dst) const override;
-    nn::GeneralResult<void> copyFrom(const nn::Memory& src,
+    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:
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
index 74a6534..b677c62 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
@@ -59,7 +59,6 @@
 GeneralResult<ErrorStatus> convert(const hal::V1_3::ErrorStatus& errorStatus);
 
 GeneralResult<SharedHandle> convert(const hardware::hidl_handle& handle);
-GeneralResult<Memory> convert(const hardware::hidl_memory& memory);
 GeneralResult<std::vector<BufferRole>> convert(
         const hardware::hidl_vec<hal::V1_3::BufferRole>& bufferRoles);
 
@@ -100,7 +99,7 @@
 nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& errorStatus);
 
 nn::GeneralResult<hidl_handle> convert(const nn::SharedHandle& handle);
-nn::GeneralResult<hidl_memory> convert(const nn::Memory& memory);
+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);
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
index f36b6c0..84f606a 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
@@ -54,7 +54,6 @@
     const std::string& getVersionString() const override;
     nn::Version getFeatureLevel() const override;
     nn::DeviceType getType() const override;
-    bool isUpdatable() 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;
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
index 29b0c80..1d76caa 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
@@ -22,14 +22,25 @@
 #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) {
@@ -49,6 +60,21 @@
     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/Buffer.cpp b/neuralnetworks/1.3/utils/src/Buffer.cpp
index 614033e..ada5265 100644
--- a/neuralnetworks/1.3/utils/src/Buffer.cpp
+++ b/neuralnetworks/1.3/utils/src/Buffer.cpp
@@ -61,7 +61,7 @@
     return kToken;
 }
 
-nn::GeneralResult<void> Buffer::copyTo(const nn::Memory& dst) const {
+nn::GeneralResult<void> Buffer::copyTo(const nn::SharedMemory& dst) const {
     const auto hidlDst = NN_TRY(convert(dst));
 
     const auto ret = kBuffer->copyTo(hidlDst);
@@ -71,7 +71,7 @@
     return {};
 }
 
-nn::GeneralResult<void> Buffer::copyFrom(const nn::Memory& src,
+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);
diff --git a/neuralnetworks/1.3/utils/src/Callbacks.cpp b/neuralnetworks/1.3/utils/src/Callbacks.cpp
index af76e6a..8e9fb83 100644
--- a/neuralnetworks/1.3/utils/src/Callbacks.cpp
+++ b/neuralnetworks/1.3/utils/src/Callbacks.cpp
@@ -28,6 +28,7 @@
 #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>
@@ -46,6 +47,20 @@
 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) {
@@ -82,13 +97,13 @@
 
 Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus status,
                                            const sp<V1_0::IPreparedModel>& preparedModel) {
-    mData.put(V1_0::utils::prepareModelCallback(status, 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(V1_2::utils::prepareModelCallback(status, preparedModel));
+    mData.put(prepareModelCallback(status, preparedModel));
     return Void();
 }
 
diff --git a/neuralnetworks/1.3/utils/src/Conversions.cpp b/neuralnetworks/1.3/utils/src/Conversions.cpp
index 8b7db2b..e8a4f55 100644
--- a/neuralnetworks/1.3/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.3/utils/src/Conversions.cpp
@@ -38,55 +38,47 @@
 #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);
 }
 
-constexpr auto kVersion = android::nn::Version::ANDROID_R;
-
 }  // namespace
 
 namespace android::nn {
 namespace {
 
-constexpr auto validOperandType(nn::OperandType operandType) {
-    switch (operandType) {
-        case nn::OperandType::FLOAT32:
-        case nn::OperandType::INT32:
-        case nn::OperandType::UINT32:
-        case nn::OperandType::TENSOR_FLOAT32:
-        case nn::OperandType::TENSOR_INT32:
-        case nn::OperandType::TENSOR_QUANT8_ASYMM:
-        case nn::OperandType::BOOL:
-        case nn::OperandType::TENSOR_QUANT16_SYMM:
-        case nn::OperandType::TENSOR_FLOAT16:
-        case nn::OperandType::TENSOR_BOOL8:
-        case nn::OperandType::FLOAT16:
-        case nn::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
-        case nn::OperandType::TENSOR_QUANT16_ASYMM:
-        case nn::OperandType::TENSOR_QUANT8_SYMM:
-        case nn::OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
-        case nn::OperandType::SUBGRAPH:
-        case nn::OperandType::OEM:
-        case nn::OperandType::TENSOR_OEM_BYTE:
-            return true;
-    }
-    return nn::isExtension(operandType);
-}
-
 using hardware::hidl_vec;
 
 template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
         std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
         const hidl_vec<Type>& arguments) {
-    std::vector<unvalidatedConvertOutput<Type>> canonical;
+    std::vector<UnvalidatedConvertOutput<Type>> canonical;
     canonical.reserve(arguments.size());
     for (const auto& argument : arguments) {
         canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
@@ -95,29 +87,16 @@
 }
 
 template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
-        const hidl_vec<Type>& arguments) {
-    return unvalidatedConvertVec(arguments);
-}
-
-template <typename Type>
-decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
     auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
-    const auto maybeVersion = validate(canonical);
-    if (!maybeVersion.has_value()) {
-        return error() << maybeVersion.error();
-    }
-    const auto version = maybeVersion.value();
-    if (version > kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
-    }
+    NN_TRY(hal::V1_3::utils::compliantVersion(canonical));
     return canonical;
 }
 
 template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> validatedConvert(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> validatedConvert(
         const hidl_vec<Type>& arguments) {
-    std::vector<unvalidatedConvertOutput<Type>> canonical;
+    std::vector<UnvalidatedConvertOutput<Type>> canonical;
     canonical.reserve(arguments.size());
     for (const auto& argument : arguments) {
         canonical.push_back(NN_TRY(validatedConvert(argument)));
@@ -143,8 +122,7 @@
     const bool validOperandTypes = std::all_of(
             capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
             [](const hal::V1_3::Capabilities::OperandPerformance& operandPerformance) {
-                const auto maybeType = unvalidatedConvert(operandPerformance.type);
-                return !maybeType.has_value() ? false : validOperandType(maybeType.value());
+                return validatedConvert(operandPerformance.type).has_value();
             });
     if (!validOperandTypes) {
         return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
@@ -217,7 +195,7 @@
 
     // Verify number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(subgraph.operands.size(), operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(subgraph.operands.size(), operations));
     CHECK(subgraph.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < subgraph.operands.size(); ++i) {
         if (subgraph.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -244,7 +222,7 @@
     return BufferRole{
             .modelIndex = bufferRole.modelIndex,
             .ioIndex = bufferRole.ioIndex,
-            .frequency = bufferRole.frequency,
+            .probability = bufferRole.frequency,
     };
 }
 
@@ -261,7 +239,7 @@
     using Discriminator = hal::V1_3::Request::MemoryPool::hidl_discriminator;
     switch (memoryPool.getDiscriminator()) {
         case Discriminator::hidlMemory:
-            return createSharedMemoryFromHidlMemory(memoryPool.hidlMemory());
+            return hal::utils::createSharedMemoryFromHidlMemory(memoryPool.hidlMemory());
         case Discriminator::token:
             return static_cast<Request::MemoryDomainToken>(memoryPool.token());
     }
@@ -276,8 +254,32 @@
     switch (optionalTimePoint.getDiscriminator()) {
         case Discriminator::none:
             return {};
-        case Discriminator::nanosecondsSinceEpoch:
-            return TimePoint{Duration{optionalTimePoint.nanosecondsSinceEpoch()}};
+        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 "
@@ -352,10 +354,6 @@
     return validatedConvert(handle);
 }
 
-GeneralResult<Memory> convert(const hardware::hidl_memory& memory) {
-    return validatedConvert(memory);
-}
-
 GeneralResult<std::vector<BufferRole>> convert(
         const hardware::hidl_vec<hal::V1_3::BufferRole>& bufferRoles) {
     return validatedConvert(bufferRoles);
@@ -386,7 +384,7 @@
     return V1_2::utils::unvalidatedConvert(handle);
 }
 
-nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
     return V1_0::utils::unvalidatedConvert(memory);
 }
 
@@ -405,26 +403,20 @@
 }
 
 template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
         std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
         const std::vector<Type>& arguments) {
-    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+    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<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
-        const std::vector<Type>& arguments) {
-    return unvalidatedConvertVec(arguments);
-}
-
-nn::GeneralResult<Request::MemoryPool> makeMemoryPool(const nn::Memory& memory) {
+nn::GeneralResult<Request::MemoryPool> makeMemoryPool(const nn::SharedMemory& memory) {
     Request::MemoryPool ret;
     ret.hidlMemory(NN_TRY(unvalidatedConvert(memory)));
     return ret;
@@ -443,22 +435,15 @@
 using utils::unvalidatedConvert;
 
 template <typename Type>
-decltype(unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
-    const auto maybeVersion = nn::validate(canonical);
-    if (!maybeVersion.has_value()) {
-        return nn::error() << maybeVersion.error();
-    }
-    const auto version = maybeVersion.value();
-    if (version > kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
-    }
+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(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> validatedConvert(
         const std::vector<Type>& arguments) {
-    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+    hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
     for (size_t i = 0; i < arguments.size(); ++i) {
         halObject[i] = NN_TRY(validatedConvert(arguments[i]));
     }
@@ -486,7 +471,7 @@
                  capabilities.operandPerformance.asVector().end(),
                  std::back_inserter(operandPerformance),
                  [](const nn::Capabilities::OperandPerformance& operandPerformance) {
-                     return nn::validOperandType(operandPerformance.type);
+                     return compliantVersion(operandPerformance.type).has_value();
                  });
 
     return Capabilities{
@@ -559,7 +544,7 @@
 
     // Update number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(operands.size(), subgraph.operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), subgraph.operations));
     CHECK(operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < operands.size(); ++i) {
         operands[i].numberOfConsumers = numberOfConsumers[i];
@@ -581,7 +566,7 @@
     return BufferRole{
             .modelIndex = bufferRole.modelIndex,
             .ioIndex = bufferRole.ioIndex,
-            .frequency = bufferRole.frequency,
+            .frequency = bufferRole.probability,
     };
 }
 
@@ -605,9 +590,33 @@
 
 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 count = optionalTimePoint.value().time_since_epoch().count();
+        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;
@@ -677,7 +686,7 @@
     return validatedConvert(handle);
 }
 
-nn::GeneralResult<hidl_memory> convert(const nn::Memory& memory) {
+nn::GeneralResult<hidl_memory> convert(const nn::SharedMemory& memory) {
     return validatedConvert(memory);
 }
 
diff --git a/neuralnetworks/1.3/utils/src/Device.cpp b/neuralnetworks/1.3/utils/src/Device.cpp
index 87c9f32..d710b85 100644
--- a/neuralnetworks/1.3/utils/src/Device.cpp
+++ b/neuralnetworks/1.3/utils/src/Device.cpp
@@ -150,10 +150,6 @@
     return kDeviceType;
 }
 
-bool Device::isUpdatable() const {
-    return false;
-}
-
 const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
     return kExtensions;
 }
diff --git a/neuralnetworks/1.3/utils/src/PreparedModel.cpp b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
index 725e4f5..fd7f8f2 100644
--- a/neuralnetworks/1.3/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
@@ -29,8 +29,9 @@
 #include <nnapi/Result.h>
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
-#include <nnapi/hal/1.0/Burst.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>
@@ -199,7 +200,17 @@
 }
 
 nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
-    return V1_0::utils::Burst::create(shared_from_this());
+    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(kPreparedModel, std::move(fallback),
+                                                         pollingTimeWindow);
 }
 
 std::any PreparedModel::getUnderlyingResource() const {
diff --git a/neuralnetworks/1.3/utils/test/DeviceTest.cpp b/neuralnetworks/1.3/utils/test/DeviceTest.cpp
index f260990..2d1b2f2 100644
--- a/neuralnetworks/1.3/utils/test/DeviceTest.cpp
+++ b/neuralnetworks/1.3/utils/test/DeviceTest.cpp
@@ -794,7 +794,7 @@
     EXPECT_NE(result.value(), nullptr);
 }
 
-TEST(DeviceTest, prepareModelFromCacheError) {
+TEST(DeviceTest, prepareModelFromCacheLaunchError) {
     // setup call
     const auto mockDevice = createMockDevice();
     const auto device = Device::create(kName, mockDevice).value();
@@ -812,6 +812,23 @@
     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();
diff --git a/neuralnetworks/1.3/utils/test/MockBuffer.h b/neuralnetworks/1.3/utils/test/MockBuffer.h
index fb31b51..a67c5f6 100644
--- a/neuralnetworks/1.3/utils/test/MockBuffer.h
+++ b/neuralnetworks/1.3/utils/test/MockBuffer.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER
+#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>
@@ -40,4 +40,4 @@
 
 }  // namespace android::hardware::neuralnetworks::V1_3::utils
 
-#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER
+#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
index 85d3750..b79037f 100644
--- a/neuralnetworks/1.3/utils/test/MockDevice.h
+++ b/neuralnetworks/1.3/utils/test/MockDevice.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE
+#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>
@@ -136,4 +136,4 @@
 
 }  // namespace android::hardware::neuralnetworks::V1_3::utils
 
-#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE
+#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
index fc08a7f..04c0a92 100644
--- a/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h
+++ b/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
+#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>
@@ -39,4 +39,4 @@
 
 }  // namespace android::hardware::neuralnetworks::V1_3::utils
 
-#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
+#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
index e441524..ef64fa4 100644
--- a/neuralnetworks/1.3/utils/test/MockPreparedModel.h
+++ b/neuralnetworks/1.3/utils/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL
+#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>
@@ -118,4 +118,4 @@
 
 }  // namespace android::hardware::neuralnetworks::V1_3::utils
 
-#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL
+#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
index 11796dd..5303c2a 100644
--- a/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "MockBurstContext.h"
 #include "MockFencedExecutionCallback.h"
 #include "MockPreparedModel.h"
 
@@ -96,6 +97,17 @@
         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); };
@@ -450,7 +462,76 @@
     EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
 }
 
-// TODO: test burst execution if/when it is added to nn::IPreparedModel.
+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
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/TEST_MAPPING b/neuralnetworks/TEST_MAPPING
index de84624..d296828 100644
--- a/neuralnetworks/TEST_MAPPING
+++ b/neuralnetworks/TEST_MAPPING
@@ -16,6 +16,9 @@
       "name": "neuralnetworks_utils_hal_1_3_test"
     },
     {
+      "name": "neuralnetworks_utils_hal_aidl_test"
+    },
+    {
       "name": "VtsHalNeuralnetworksV1_0TargetTest",
       "options": [
         {
@@ -60,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/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferDesc.aidl
similarity index 60%
copy from security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl
copy to neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferDesc.aidl
index cef8eca..05cec76 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferDesc.aidl
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -16,8 +31,8 @@
 // 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;
+package android.hardware.neuralnetworks;
 @VintfStability
-parcelable ByteArray {
-  byte[] data;
+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/security/keymint/aidl/android/hardware/security/keymint/KeyParameterArray.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferDesc.aidl
similarity index 60%
copy from security/keymint/aidl/android/hardware/security/keymint/KeyParameterArray.aidl
copy to neuralnetworks/aidl/android/hardware/neuralnetworks/BufferDesc.aidl
index acab435..bec7e86 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyParameterArray.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferDesc.aidl
@@ -14,18 +14,17 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
-
-import android.hardware.security.keymint.KeyParameter;
+package android.hardware.neuralnetworks;
 
 /**
- * Identifies the key authorization parameters to be used with keyMint.  This is usually
- * provided as an array of KeyParameters to IKeyMintDevice or Operation.
+ * A buffer descriptor. Describes the properties of a buffer.
  */
 @VintfStability
-parcelable KeyParameterArray {
+parcelable BufferDesc {
     /**
-     * Identify list of key parameters corresponding to a particular key blob.
+     * 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.
      */
-    KeyParameter[] params;
+    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/security/keymint/aidl/android/hardware/security/keymint/KeyParameterArray.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
similarity index 60%
rename from security/keymint/aidl/android/hardware/security/keymint/KeyParameterArray.aidl
rename to neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
index acab435..b8e3449 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyParameterArray.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
@@ -14,18 +14,23 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
-
-import android.hardware.security.keymint.KeyParameter;
+package android.hardware.neuralnetworks;
 
 /**
- * Identifies the key authorization parameters to be used with keyMint.  This is usually
- * provided as an array of KeyParameters to IKeyMintDevice or Operation.
+ * Information about an extension operand type.
  */
 @VintfStability
-parcelable KeyParameterArray {
+parcelable ExtensionOperandTypeInformation {
     /**
-     * Identify list of key parameters corresponding to a particular key blob.
+     * The extension operand type.
      */
-    KeyParameter[] params;
+    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/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/FusedActivationFunc.aidl
similarity index 64%
copy from security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
copy to neuralnetworks/aidl/android/hardware/neuralnetworks/FusedActivationFunc.aidl
index c3b402e..df015ca 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/FusedActivationFunc.aidl
@@ -14,13 +14,28 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
+package android.hardware.neuralnetworks;
 
 /**
- * This is used to contain a byte[], to make out parameters of byte arrays
- * more convenient for callers.
+ * Fused activation function types.
  */
 @VintfStability
-parcelable ByteArray {
-    byte[] data;
+@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/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
similarity index 73%
copy from security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
copy to neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
index c3b402e..878b0ad 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.IPreparedModel;
 
 /**
- * This is used to contain a byte[], to make out parameters of byte arrays
- * more convenient for callers.
+ * A parcelable for passing a vector of IPreparedModel objects.
  */
 @VintfStability
-parcelable ByteArray {
-    byte[] data;
+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/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl
similarity index 72%
copy from security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
copy to neuralnetworks/aidl/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl
index c3b402e..1ca2676 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
+package android.hardware.neuralnetworks;
 
 /**
- * This is used to contain a byte[], to make out parameters of byte arrays
- * more convenient for callers.
+ * Structure indicating how many files for model and numDataCache cache the driver needs to cache a
+ * single prepared model.
  */
 @VintfStability
-parcelable ByteArray {
-    byte[] data;
+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/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl
similarity index 61%
copy from security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
copy to neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl
index c3b402e..7f53967 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl
@@ -14,13 +14,17 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.OperandType;
+import android.hardware.neuralnetworks.PerformanceInfo;
 
 /**
- * This is used to contain a byte[], to make out parameters of byte arrays
- * more convenient for callers.
+ * 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 ByteArray {
-    byte[] data;
+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/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OutputShape.aidl
similarity index 66%
copy from security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
copy to neuralnetworks/aidl/android/hardware/neuralnetworks/OutputShape.aidl
index c3b402e..f90a613 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OutputShape.aidl
@@ -14,13 +14,19 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
+package android.hardware.neuralnetworks;
 
 /**
- * This is used to contain a byte[], to make out parameters of byte arrays
- * more convenient for callers.
+ * Describes the shape information of an output operand after execution.
  */
 @VintfStability
-parcelable ByteArray {
-    byte[] data;
+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/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Priority.aidl
similarity index 76%
rename from security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
rename to neuralnetworks/aidl/android/hardware/neuralnetworks/Priority.aidl
index c3b402e..7dbf8e9 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Priority.aidl
@@ -14,13 +14,15 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
+package android.hardware.neuralnetworks;
 
 /**
- * This is used to contain a byte[], to make out parameters of byte arrays
- * more convenient for callers.
+ * Priority given to a prepared model for execution.
  */
 @VintfStability
-parcelable ByteArray {
-    byte[] data;
+@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/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestMemoryPool.aidl
similarity index 62%
copy from security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
copy to neuralnetworks/aidl/android/hardware/neuralnetworks/RequestMemoryPool.aidl
index c3b402e..faca2fe 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestMemoryPool.aidl
@@ -14,13 +14,22 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.Memory;
 
 /**
- * This is used to contain a byte[], to make out parameters of byte arrays
- * more convenient for callers.
+ * A memory pool.
  */
 @VintfStability
-parcelable ByteArray {
-    byte[] data;
+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/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
similarity index 65%
copy from security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
copy to neuralnetworks/aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
index c3b402e..eb47df0 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
@@ -14,13 +14,19 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
+package android.hardware.neuralnetworks;
 
 /**
- * This is used to contain a byte[], to make out parameters of byte arrays
- * more convenient for callers.
+ * Parameters for TENSOR_QUANT8_SYMM_PER_CHANNEL operand.
  */
 @VintfStability
-parcelable ByteArray {
-    byte[] data;
+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..008e4e4
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h
@@ -0,0 +1,111 @@
+/*
+ * 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 {
+    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;
+
+  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..5eab9ff
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h
@@ -0,0 +1,192 @@
+/*
+ * 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 ::aidl::android::hardware::common::NativeHandle& handle);
+GeneralResult<SyncFence> unvalidatedConvert(const ndk::ScopedFileDescriptor& syncFence);
+
+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<SyncFence> convert(const ndk::ScopedFileDescriptor& syncFence);
+
+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<common::NativeHandle> unvalidatedConvert(const nn::SharedHandle& sharedHandle);
+nn::GeneralResult<ndk::ScopedFileDescriptor> unvalidatedConvertCache(
+        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/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..abce6cc
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
@@ -0,0 +1,68 @@
+/*
+ * 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 <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 {
+    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::SharedBurst> configureExecutionBurst() const override;
+
+    std::any getUnderlyingResource() const override;
+
+  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..0b475bc
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Burst.cpp
@@ -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.
+ */
+
+#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/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 {
+
+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 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(); });
+
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::makeExecutionFailure(
+            hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared)));
+
+    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(request.pools.size());
+    holds.reserve(request.pools.size());
+    for (const auto& memoryPool : request.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(request.pools.size(), memoryIdentifierTokens.size());
+
+    ExecutionResult executionResult;
+    const auto ret =
+            kBurst->executeSynchronously(aidlRequest, memoryIdentifierTokens, aidlMeasure,
+                                         aidlDeadline, aidlLoopTimeoutDuration, &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)));
+
+    NN_TRY(hal::utils::makeExecutionFailure(
+            hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
+
+    return std::make_pair(std::move(outputShapes), timing);
+}
+
+}  // 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..4b263ee
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Conversions.cpp
@@ -0,0 +1,1097 @@
+/*
+ * 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;
+}
+
+GeneralResult<Handle> unvalidatedConvertHelper(const NativeHandle& aidlNativeHandle) {
+    std::vector<base::unique_fd> fds;
+    fds.reserve(aidlNativeHandle.fds.size());
+    for (const auto& fd : aidlNativeHandle.fds) {
+        auto duplicatedFd = NN_TRY(dupFd(fd.get()));
+        fds.emplace_back(duplicatedFd.release());
+    }
+
+    return Handle{.fds = std::move(fds), .ints = aidlNativeHandle.ints};
+}
+
+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<SharedHandle> unvalidatedConvert(const NativeHandle& aidlNativeHandle) {
+    return std::make_shared<const Handle>(NN_TRY(unvalidatedConvertHelper(aidlNativeHandle)));
+}
+
+GeneralResult<std::vector<Operation>> unvalidatedConvert(
+        const std::vector<aidl_hal::Operation>& operations) {
+    return unvalidatedConvertVec(operations);
+}
+
+GeneralResult<SyncFence> unvalidatedConvert(const ndk::ScopedFileDescriptor& syncFence) {
+    auto duplicatedFd = NN_TRY(dupFd(syncFence.get()));
+    return SyncFence::create(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<SyncFence> convert(const ndk::ScopedFileDescriptor& syncFence) {
+    return validatedConvert(syncFence);
+}
+
+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;
+}
+
+nn::GeneralResult<common::NativeHandle> unvalidatedConvert(const nn::Handle& handle) {
+    common::NativeHandle aidlNativeHandle;
+    aidlNativeHandle.fds.reserve(handle.fds.size());
+    for (const auto& fd : handle.fds) {
+        auto duplicatedFd = NN_TRY(nn::dupFd(fd.get()));
+        aidlNativeHandle.fds.emplace_back(duplicatedFd.release());
+    }
+    aidlNativeHandle.ints = handle.ints;
+    return aidlNativeHandle;
+}
+
+// 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<common::NativeHandle> unvalidatedConvert(const nn::SharedHandle& sharedHandle) {
+    CHECK(sharedHandle != nullptr);
+    return unvalidatedConvert(*sharedHandle);
+}
+
+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> unvalidatedConvertCache(
+        const nn::SharedHandle& handle) {
+    if (handle->ints.size() != 0) {
+        NN_ERROR() << "Cache handle must not contain ints";
+    }
+    if (handle->fds.size() != 1) {
+        NN_ERROR() << "Cache handle must contain exactly one fd but contains "
+                   << handle->fds.size();
+    }
+    auto duplicatedFd = NN_TRY(nn::dupFd(handle->fds.front().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) {
+    const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(cacheHandles)));
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    std::vector<ndk::ScopedFileDescriptor> cacheFds;
+    cacheFds.reserve(cacheHandles.size());
+    for (const auto& cacheHandle : cacheHandles) {
+        cacheFds.push_back(NN_TRY(unvalidatedConvertCache(cacheHandle)));
+    }
+    return cacheFds;
+}
+
+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..0fd453b
--- /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.numDataCache, numberOfCacheFiles.numModelCache);
+}
+
+}  // 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/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..003965b
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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 "Utils.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;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::makeExecutionFailure(
+            hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared)));
+
+    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)));
+
+    ExecutionResult executionResult;
+    const auto ret = kPreparedModel->executeSynchronously(
+            aidlRequest, aidlMeasure, aidlDeadline, aidlLoopTimeoutDuration, &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)));
+
+    NN_TRY(hal::utils::makeExecutionFailure(
+            hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
+
+    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;
+    const nn::Request& requestInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared));
+
+    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));
+
+    FencedExecutionResult result;
+    const auto ret = kPreparedModel->executeFenced(aidlRequest, aidlWaitFor, aidlMeasure,
+                                                   aidlDeadline, aidlLoopTimeoutDuration,
+                                                   aidlTimeoutDurationAfterFence, &result);
+    HANDLE_ASTATUS(ret) << "executeFenced failed";
+
+    auto resultSyncFence = nn::SyncFence::createAsSignaled();
+    if (result.syncFence.get() != -1) {
+        resultSyncFence = NN_TRY(nn::convert(result.syncFence));
+    }
+
+    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 (maybeRequestInShared.has_value()) {
+        const auto state = resultSyncFence.syncWait({});
+        if (state != nn::SyncFence::FenceState::SIGNALED) {
+            return NN_ERROR() << "syncWait failed with " << state;
+        }
+        NN_TRY(hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared));
+    }
+
+    // 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::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..e53b0a8
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/DeviceTest.cpp
@@ -0,0 +1,861 @@
+/*
+ * 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,
+                                                    .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, 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..ff98a7d
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
@@ -0,0 +1,337 @@
+/*
+ * 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/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, 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..e0b529f
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp
@@ -0,0 +1,1177 @@
+/*
+ * 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;
+    }
+
+    void prepareModelFromCache(const std::vector<ndk::ScopedFileDescriptor>& modelCache,
+                               const std::vector<ndk::ScopedFileDescriptor>& dataCache,
+                               std::shared_ptr<IPreparedModel>* preparedModel,
+                               ErrorStatus* status) {
+        // 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));
+        const auto prepareLaunchStatus = kDevice->prepareModelFromCache(
+                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();
+    }
+
+    // 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;
+    uint32_t 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);
+        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);
+        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.
+        preparedModel = nullptr;
+        ErrorStatus status;
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+        if (status != ErrorStatus::INVALID_ARGUMENT) {
+            ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        }
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+
+    // 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.
+        preparedModel = nullptr;
+        ErrorStatus status;
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+        if (status != ErrorStatus::INVALID_ARGUMENT) {
+            ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        }
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+
+    // 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.
+        preparedModel = nullptr;
+        ErrorStatus status;
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+        if (status != ErrorStatus::INVALID_ARGUMENT) {
+            ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        }
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+
+    // 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.
+        preparedModel = nullptr;
+        ErrorStatus status;
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+        if (status != ErrorStatus::INVALID_ARGUMENT) {
+            ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        }
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+}
+
+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::shared_ptr<IPreparedModel> preparedModel = nullptr;
+        ErrorStatus status;
+        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();
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+        if (status != ErrorStatus::GENERAL_FAILURE) {
+            ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+        }
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+
+    // Test with number of model cache files smaller than mNumModelCache.
+    if (mModelCache.size() > 0) {
+        std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+        ErrorStatus status;
+        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);
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+        if (status != ErrorStatus::GENERAL_FAILURE) {
+            ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+        }
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+
+    // Test with number of data cache files greater than mNumDataCache.
+    {
+        std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+        ErrorStatus status;
+        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();
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+        if (status != ErrorStatus::GENERAL_FAILURE) {
+            ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+        }
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+
+    // Test with number of data cache files smaller than mNumDataCache.
+    if (mDataCache.size() > 0) {
+        std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+        ErrorStatus status;
+        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);
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+        if (status != ErrorStatus::GENERAL_FAILURE) {
+            ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+        }
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+}
+
+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.
+        preparedModel = nullptr;
+        ErrorStatus status;
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+        if (status != ErrorStatus::INVALID_ARGUMENT) {
+            ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        }
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+
+    // 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.
+        preparedModel = nullptr;
+        ErrorStatus status;
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+        if (status != ErrorStatus::INVALID_ARGUMENT) {
+            ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        }
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+}
+
+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::shared_ptr<IPreparedModel> preparedModel = nullptr;
+        ErrorStatus status;
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        modelCacheMode[i] = AccessMode::WRITE_ONLY;
+        createCacheFds(mModelCache, modelCacheMode, &modelCache);
+        createCacheFds(mDataCache, dataCacheMode, &dataCache);
+        modelCacheMode[i] = AccessMode::READ_WRITE;
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+
+    // Go through each handle in data cache, test with invalid access mode.
+    for (uint32_t i = 0; i < mNumDataCache; i++) {
+        std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+        ErrorStatus status;
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        dataCacheMode[i] = AccessMode::WRITE_ONLY;
+        createCacheFds(mModelCache, modelCacheMode, &modelCache);
+        createCacheFds(mDataCache, dataCacheMode, &dataCache);
+        dataCacheMode[i] = AccessMode::READ_WRITE;
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+}
+
+// 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);
+
+            // 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);
+            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..d3b041d
--- /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));
+        mOutputMemory = TestAshmem::create(std::max<size_t>(outputSize, 1));
+    }
+    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..3c7f5f7
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/Utils.cpp
@@ -0,0 +1,253 @@
+/*
+ * 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 <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) {
+    auto ashmem = std::make_unique<TestAshmem>(size);
+    return ashmem->mIsValid ? std::move(ashmem) : nullptr;
+}
+
+void TestAshmem::initialize(uint32_t size) {
+    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);
+    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..77085a7
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/Utils.h
@@ -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.
+ */
+
+#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:
+    static std::unique_ptr<TestAshmem> create(uint32_t size);
+
+    // 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) { initialize(size); }
+
+  private:
+    void initialize(uint32_t size);
+    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/README.md b/neuralnetworks/utils/README.md
index 45ca0b4..87b3f9f 100644
--- a/neuralnetworks/utils/README.md
+++ b/neuralnetworks/utils/README.md
@@ -49,7 +49,9 @@
 (i.e., not as a nested class) or used in a subsequent version of the NN HAL. Prefer using `convert`
 over `unvalidatedConvert`.
 
-# HIDL Interface Lifetimes across Processes
+# Interface Lifetimes across Processes
+
+## HIDL
 
 Some notes about HIDL interface objects and lifetimes across processes:
 
@@ -68,7 +70,20 @@
 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`.
 
-# Protecting Asynchronous Calls across HIDL
+## 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:
 
@@ -95,3 +110,17 @@
 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
index 6c491ae..2ed1e40 100644
--- a/neuralnetworks/utils/common/Android.bp
+++ b/neuralnetworks/utils/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: "neuralnetworks_utils_hal_common",
     defaults: ["neuralnetworks_utils_defaults"],
@@ -22,10 +31,14 @@
     export_include_dirs: ["include"],
     cflags: ["-Wthread-safety"],
     static_libs: [
+        "libarect",
         "neuralnetworks_types",
     ],
     shared_libs: [
+        "android.hardware.neuralnetworks-V1-ndk_platform",
         "libhidlbase",
+        "libnativewindow",
+        "libbinder_ndk",
     ],
 }
 
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
index b3989e5..8fe6b90 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
@@ -24,15 +24,23 @@
 #include <functional>
 #include <vector>
 
-// Shorthand
+// Shorthands
 namespace android::hardware::neuralnetworks {
 namespace hal = ::android::hardware::neuralnetworks;
 }  // namespace android::hardware::neuralnetworks
 
-// Shorthand
+// 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 {
 
@@ -65,13 +73,15 @@
 nn::GeneralResult<void> unflushDataFromSharedToPointer(
         const nn::Request& request, const std::optional<nn::Request>& maybeRequestInShared);
 
-std::vector<uint32_t> countNumberOfConsumers(size_t numberOfOperands,
-                                             const std::vector<nn::Operation>& operations);
+nn::GeneralResult<std::vector<uint32_t>> countNumberOfConsumers(
+        size_t numberOfOperands, const std::vector<nn::Operation>& operations);
 
-nn::GeneralResult<nn::Memory> createSharedMemoryFromHidlMemory(const hidl_memory& memory);
+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::SharedHandle& handle);
-nn::GeneralResult<nn::SharedHandle> sharedHandleFromNativeHandle(const native_handle_t* handle);
+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);
 
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h b/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
index 95a20a8..209b663 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
@@ -14,6 +14,9 @@
  * 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>
@@ -50,7 +53,8 @@
     })
 
 template <typename Type>
-nn::GeneralResult<Type> makeGeneralFailure(nn::Result<Type> result, nn::ErrorStatus status) {
+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();
     }
@@ -75,7 +79,8 @@
 }
 
 template <typename Type>
-nn::ExecutionResult<Type> makeExecutionFailure(nn::Result<Type> result, nn::ErrorStatus status) {
+nn::ExecutionResult<Type> makeExecutionFailure(
+        nn::Result<Type> result, nn::ErrorStatus status = nn::ErrorStatus::GENERAL_FAILURE) {
     return makeExecutionFailure(makeGeneralFailure(result, status));
 }
 
@@ -86,4 +91,6 @@
     } else                                                              \
         return NN_ERROR(canonical)
 
-}  // namespace android::hardware::neuralnetworks::utils
\ No newline at end of file
+}  // 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
index 8c04b88..0e98c2e 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBuffer.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBuffer.h
@@ -31,9 +31,9 @@
   public:
     nn::Request::MemoryDomainToken getToken() const override;
 
-    nn::GeneralResult<void> copyTo(const nn::Memory& dst) const override;
+    nn::GeneralResult<void> copyTo(const nn::SharedMemory& dst) const override;
 
-    nn::GeneralResult<void> copyFrom(const nn::Memory& src,
+    nn::GeneralResult<void> copyFrom(const nn::SharedMemory& src,
                                      const nn::Dimensions& dimensions) const override;
 };
 
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
index 83e60b6..17b3fd9 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
@@ -29,10 +29,12 @@
 
 class InvalidBurst final : public nn::IBurst {
   public:
-    OptionalCacheHold cacheMemory(const nn::Memory& memory) const override;
+    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 override;
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
 };
 
 }  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h
index d843526..5e62b9a 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h
@@ -32,7 +32,7 @@
 class InvalidDevice final : public nn::IDevice {
   public:
     InvalidDevice(std::string name, std::string versionString, nn::Version featureLevel,
-                  nn::DeviceType type, bool isUpdatable, std::vector<nn::Extension> extensions,
+                  nn::DeviceType type, std::vector<nn::Extension> extensions,
                   nn::Capabilities capabilities,
                   std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded);
 
@@ -40,7 +40,6 @@
     const std::string& getVersionString() const override;
     nn::Version getFeatureLevel() const override;
     nn::DeviceType getType() const override;
-    bool isUpdatable() 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;
@@ -71,7 +70,6 @@
     const std::string kVersionString;
     const nn::Version kFeatureLevel;
     const nn::DeviceType kType;
-    const bool kIsUpdatable;
     const std::vector<nn::Extension> kExtensions;
     const nn::Capabilities kCapabilities;
     const std::pair<uint32_t, uint32_t> kNumberOfCacheFilesNeeded;
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h b/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
index c921885..05110bc 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
@@ -56,7 +56,7 @@
 // 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;
+    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.
@@ -64,6 +64,7 @@
 
   private:
     mutable std::mutex mMutex;
+    mutable bool mIsDeadObject GUARDED_BY(mMutex) = false;
     mutable std::vector<IProtectedCallback*> mObjects GUARDED_BY(mMutex);
 };
 
@@ -78,14 +79,21 @@
     ~DeathHandler();
 
     using Cleanup = std::function<void()>;
+    using Hold = base::ScopeGuard<Cleanup>;
+
     // Precondition: `killable` must be non-null.
-    [[nodiscard]] base::ScopeGuard<Cleanup> protectCallback(IProtectedCallback* killable) const;
+    // `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> kObject;
-    sp<DeathRecipient> kDeathRecipient;
+    sp<hidl::base::V1_0::IBase> mObject;
+    sp<DeathRecipient> mDeathRecipient;
 };
 
 }  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h
index d2c2469..c8ca6f2 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h
@@ -46,9 +46,9 @@
 
     nn::Request::MemoryDomainToken getToken() const override;
 
-    nn::GeneralResult<void> copyTo(const nn::Memory& dst) const override;
+    nn::GeneralResult<void> copyTo(const nn::SharedMemory& dst) const override;
 
-    nn::GeneralResult<void> copyFrom(const nn::Memory& src,
+    nn::GeneralResult<void> copyFrom(const nn::SharedMemory& src,
                                      const nn::Dimensions& dimensions) const override;
 
   private:
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
index 0df287f..c92cc41 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
@@ -44,10 +44,12 @@
     nn::SharedBurst getBurst() const;
     nn::GeneralResult<nn::SharedBurst> recover(const nn::IBurst* failingBurst) const;
 
-    OptionalCacheHold cacheMemory(const nn::Memory& memory) const override;
+    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 override;
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
 
   private:
     const Factory kMakeBurst;
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
index 8199c52..84ae799 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
@@ -53,7 +53,6 @@
     const std::string& getVersionString() const override;
     nn::Version getFeatureLevel() const override;
     nn::DeviceType getType() const override;
-    bool isUpdatable() 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;
diff --git a/neuralnetworks/utils/common/src/CommonUtils.cpp b/neuralnetworks/utils/common/src/CommonUtils.cpp
index c04c8df..4d26795 100644
--- a/neuralnetworks/utils/common/src/CommonUtils.cpp
+++ b/neuralnetworks/utils/common/src/CommonUtils.cpp
@@ -20,11 +20,14 @@
 
 #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>
@@ -86,6 +89,59 @@
                   });
 }
 
+nn::GeneralResult<hidl_handle> createNativeHandleFrom(base::unique_fd fd,
+                                                      const std::vector<int32_t>& ints) {
+    constexpr size_t kIntMax = std::numeric_limits<int>::max();
+    CHECK_LE(ints.size(), kIntMax);
+    native_handle_t* nativeHandle = native_handle_create(1, static_cast<int>(ints.size()));
+    if (nativeHandle == nullptr) {
+        return NN_ERROR() << "Failed to create native_handle";
+    }
+
+    nativeHandle->data[0] = fd.release();
+    std::copy(ints.begin(), ints.end(), nativeHandle->data + 1);
+
+    hidl_handle handle;
+    handle.setTo(nativeHandle, /*shouldOwn=*/true);
+    return handle;
+}
+
+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(hidlHandleFromSharedHandle(memory.handle)), memory.size);
+}
+
 }  // anonymous namespace
 
 nn::Capabilities::OperandPerformanceTable makeQuantized8PerformanceConsistentWithP(
@@ -203,13 +259,13 @@
 nn::GeneralResult<void> unflushDataFromSharedToPointer(
         const nn::Request& request, const std::optional<nn::Request>& maybeRequestInShared) {
     if (!maybeRequestInShared.has_value() || maybeRequestInShared->pools.empty() ||
-        !std::holds_alternative<nn::Memory>(maybeRequestInShared->pools.back())) {
+        !std::holds_alternative<nn::SharedMemory>(maybeRequestInShared->pools.back())) {
         return {};
     }
     const auto& requestInShared = *maybeRequestInShared;
 
     // Map the memory.
-    const auto& outputMemory = std::get<nn::Memory>(requestInShared.pools.back());
+    const auto& outputMemory = std::get<nn::SharedMemory>(requestInShared.pools.back());
     const auto [pointer, size, context] = NN_TRY(map(outputMemory));
     const uint8_t* constantPointer =
             std::visit([](const auto& o) { return static_cast<const uint8_t*>(o); }, pointer);
@@ -243,49 +299,148 @@
     return {};
 }
 
-std::vector<uint32_t> countNumberOfConsumers(size_t numberOfOperands,
-                                             const std::vector<nn::Operation>& operations) {
-    return nn::countNumberOfConsumers(numberOfOperands, operations);
+nn::GeneralResult<std::vector<uint32_t>> countNumberOfConsumers(
+        size_t numberOfOperands, const std::vector<nn::Operation>& operations) {
+    return makeGeneralFailure(nn::countNumberOfConsumers(numberOfOperands, operations));
 }
 
-nn::GeneralResult<hidl_handle> hidlHandleFromSharedHandle(const nn::SharedHandle& handle) {
-    if (handle == nullptr) {
-        return {};
+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(sharedHandleFromNativeHandle(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) {
     std::vector<base::unique_fd> fds;
-    fds.reserve(handle->fds.size());
-    for (const auto& fd : handle->fds) {
-        int dupFd = dup(fd);
+    fds.reserve(handle.fds.size());
+    for (const auto& fd : handle.fds) {
+        const int dupFd = dup(fd);
         if (dupFd == -1) {
             return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Failed to dup the fd";
         }
         fds.emplace_back(dupFd);
     }
 
-    native_handle_t* nativeHandle = native_handle_create(handle->fds.size(), handle->ints.size());
+    constexpr size_t kIntMax = std::numeric_limits<int>::max();
+    CHECK_LE(handle.fds.size(), kIntMax);
+    CHECK_LE(handle.ints.size(), kIntMax);
+    native_handle_t* nativeHandle = native_handle_create(static_cast<int>(handle.fds.size()),
+                                                         static_cast<int>(handle.ints.size()));
     if (nativeHandle == nullptr) {
         return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Failed to create native_handle";
     }
     for (size_t i = 0; i < fds.size(); ++i) {
         nativeHandle->data[i] = fds[i].release();
     }
-    std::copy(handle->ints.begin(), handle->ints.end(), &nativeHandle->data[nativeHandle->numFds]);
+    std::copy(handle.ints.begin(), handle.ints.end(), &nativeHandle->data[nativeHandle->numFds]);
 
     hidl_handle hidlHandle;
     hidlHandle.setTo(nativeHandle, /*shouldOwn=*/true);
     return hidlHandle;
 }
 
-nn::GeneralResult<nn::SharedHandle> sharedHandleFromNativeHandle(const native_handle_t* handle) {
+nn::GeneralResult<nn::Handle> sharedHandleFromNativeHandle(const native_handle_t* handle) {
     if (handle == nullptr) {
-        return nullptr;
+        return NN_ERROR() << "sharedHandleFromNativeHandle failed because handle is nullptr";
     }
 
     std::vector<base::unique_fd> fds;
     fds.reserve(handle->numFds);
     for (int i = 0; i < handle->numFds; ++i) {
-        int dupFd = dup(handle->data[i]);
+        const int dupFd = dup(handle->data[i]);
         if (dupFd == -1) {
             return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Failed to dup the fd";
         }
@@ -295,18 +450,18 @@
     std::vector<int> ints(&handle->data[handle->numFds],
                           &handle->data[handle->numFds + handle->numInts]);
 
-    return std::make_shared<const nn::Handle>(nn::Handle{
-            .fds = std::move(fds),
-            .ints = std::move(ints),
-    });
+    return nn::Handle{.fds = std::move(fds), .ints = std::move(ints)};
 }
 
 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) {
-        handles[i] =
-                NN_TRY(hal::utils::hidlHandleFromSharedHandle(syncFences[i].getSharedHandle()));
+        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;
 }
diff --git a/neuralnetworks/utils/common/src/InvalidBuffer.cpp b/neuralnetworks/utils/common/src/InvalidBuffer.cpp
index c6f75d7..e73001d 100644
--- a/neuralnetworks/utils/common/src/InvalidBuffer.cpp
+++ b/neuralnetworks/utils/common/src/InvalidBuffer.cpp
@@ -30,11 +30,11 @@
     return nn::Request::MemoryDomainToken{};
 }
 
-nn::GeneralResult<void> InvalidBuffer::copyTo(const nn::Memory& /*dst*/) const {
+nn::GeneralResult<void> InvalidBuffer::copyTo(const nn::SharedMemory& /*dst*/) const {
     return NN_ERROR() << "InvalidBuffer";
 }
 
-nn::GeneralResult<void> InvalidBuffer::copyFrom(const nn::Memory& /*src*/,
+nn::GeneralResult<void> InvalidBuffer::copyFrom(const nn::SharedMemory& /*src*/,
                                                 const nn::Dimensions& /*dimensions*/) const {
     return NN_ERROR() << "InvalidBuffer";
 }
diff --git a/neuralnetworks/utils/common/src/InvalidBurst.cpp b/neuralnetworks/utils/common/src/InvalidBurst.cpp
index 4ca6603..0c34f05 100644
--- a/neuralnetworks/utils/common/src/InvalidBurst.cpp
+++ b/neuralnetworks/utils/common/src/InvalidBurst.cpp
@@ -26,12 +26,15 @@
 
 namespace android::hardware::neuralnetworks::utils {
 
-InvalidBurst::OptionalCacheHold InvalidBurst::cacheMemory(const nn::Memory& /*memory*/) const {
+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 {
+        const nn::Request& /*request*/, nn::MeasureTiming /*measure*/,
+        const nn::OptionalTimePoint& /*deadline*/,
+        const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
     return NN_ERROR() << "InvalidBurst";
 }
 
diff --git a/neuralnetworks/utils/common/src/InvalidDevice.cpp b/neuralnetworks/utils/common/src/InvalidDevice.cpp
index 81bca7f..535ccb4 100644
--- a/neuralnetworks/utils/common/src/InvalidDevice.cpp
+++ b/neuralnetworks/utils/common/src/InvalidDevice.cpp
@@ -32,14 +32,13 @@
 namespace android::hardware::neuralnetworks::utils {
 
 InvalidDevice::InvalidDevice(std::string name, std::string versionString, nn::Version featureLevel,
-                             nn::DeviceType type, bool isUpdatable,
-                             std::vector<nn::Extension> extensions, nn::Capabilities capabilities,
+                             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),
-      kIsUpdatable(isUpdatable),
       kExtensions(std::move(extensions)),
       kCapabilities(std::move(capabilities)),
       kNumberOfCacheFilesNeeded(numberOfCacheFilesNeeded) {}
@@ -60,10 +59,6 @@
     return kType;
 }
 
-bool InvalidDevice::isUpdatable() const {
-    return kIsUpdatable;
-}
-
 const std::vector<nn::Extension>& InvalidDevice::getSupportedExtensions() const {
     return kExtensions;
 }
diff --git a/neuralnetworks/utils/common/src/ProtectCallback.cpp b/neuralnetworks/utils/common/src/ProtectCallback.cpp
index abe4cb6..18e1f3b 100644
--- a/neuralnetworks/utils/common/src/ProtectCallback.cpp
+++ b/neuralnetworks/utils/common/src/ProtectCallback.cpp
@@ -35,19 +35,25 @@
     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);
-    mObjects.push_back(killable);
+    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 removedIter = std::remove(mObjects.begin(), mObjects.end(), killable);
-    mObjects.erase(removedIter);
+    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) {
@@ -67,19 +73,16 @@
 }
 
 DeathHandler::DeathHandler(sp<hidl::base::V1_0::IBase> object, sp<DeathRecipient> deathRecipient)
-    : kObject(std::move(object)), kDeathRecipient(std::move(deathRecipient)) {
-    CHECK(kObject != nullptr);
-    CHECK(kDeathRecipient != nullptr);
+    : mObject(std::move(object)), mDeathRecipient(std::move(deathRecipient)) {
+    CHECK(mObject != nullptr);
+    CHECK(mDeathRecipient != nullptr);
 }
 
 DeathHandler::~DeathHandler() {
-    if (kObject != nullptr && kDeathRecipient != nullptr) {
-        const auto ret = kObject->unlinkToDeath(kDeathRecipient);
-        const auto maybeSuccess = handleTransportError(ret);
-        if (!maybeSuccess.has_value()) {
-            LOG(ERROR) << maybeSuccess.error().message;
-        } else if (!maybeSuccess.value()) {
-            LOG(ERROR) << "IBase::linkToDeath returned false";
+    if (mObject != nullptr && mDeathRecipient != nullptr) {
+        const auto successful = mObject->unlinkToDeath(mDeathRecipient).isOk();
+        if (!successful) {
+            LOG(ERROR) << "IBase::linkToDeath failed";
         }
     }
 }
@@ -87,9 +90,14 @@
 [[nodiscard]] base::ScopeGuard<DeathHandler::Cleanup> DeathHandler::protectCallback(
         IProtectedCallback* killable) const {
     CHECK(killable != nullptr);
-    kDeathRecipient->add(killable);
+    mDeathRecipient->add(killable);
     return base::make_scope_guard(
-            [deathRecipient = kDeathRecipient, killable] { deathRecipient->remove(killable); });
+            [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
index 47abbe2..1904375 100644
--- a/neuralnetworks/utils/common/src/ResilientBuffer.cpp
+++ b/neuralnetworks/utils/common/src/ResilientBuffer.cpp
@@ -99,12 +99,12 @@
     return getBuffer()->getToken();
 }
 
-nn::GeneralResult<void> ResilientBuffer::copyTo(const nn::Memory& dst) const {
+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::Memory& src,
+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);
diff --git a/neuralnetworks/utils/common/src/ResilientBurst.cpp b/neuralnetworks/utils/common/src/ResilientBurst.cpp
index 0d3cb33..38ccc62 100644
--- a/neuralnetworks/utils/common/src/ResilientBurst.cpp
+++ b/neuralnetworks/utils/common/src/ResilientBurst.cpp
@@ -94,14 +94,17 @@
     return mBurst;
 }
 
-ResilientBurst::OptionalCacheHold ResilientBurst::cacheMemory(const nn::Memory& memory) const {
+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 {
-    const auto fn = [&request, measure](const nn::IBurst& burst) {
-        return burst.execute(request, measure);
+        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);
 }
diff --git a/neuralnetworks/utils/common/src/ResilientDevice.cpp b/neuralnetworks/utils/common/src/ResilientDevice.cpp
index 13965af..2023c9a 100644
--- a/neuralnetworks/utils/common/src/ResilientDevice.cpp
+++ b/neuralnetworks/utils/common/src/ResilientDevice.cpp
@@ -122,14 +122,12 @@
     };
     if (compare(&IDevice::getName) || compare(&IDevice::getVersionString) ||
         compare(&IDevice::getFeatureLevel) || compare(&IDevice::getType) ||
-        compare(&IDevice::isUpdatable) || compare(&IDevice::getSupportedExtensions) ||
-        compare(&IDevice::getCapabilities)) {
+        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(),
-                mDevice->isUpdatable(), kExtensions, kCapabilities,
-                mDevice->getNumberOfCacheFilesNeeded());
+                kName, kVersionString, mDevice->getFeatureLevel(), mDevice->getType(), kExtensions,
+                kCapabilities, mDevice->getNumberOfCacheFilesNeeded());
         mIsValid = false;
     }
 
@@ -153,10 +151,6 @@
     return getDevice()->getType();
 }
 
-bool ResilientDevice::isUpdatable() const {
-    return getDevice()->isUpdatable();
-}
-
 const std::vector<nn::Extension>& ResilientDevice::getSupportedExtensions() const {
     return kExtensions;
 }
diff --git a/neuralnetworks/utils/common/test/MockBuffer.h b/neuralnetworks/utils/common/test/MockBuffer.h
index c5405fb..3599d0c 100644
--- a/neuralnetworks/utils/common/test/MockBuffer.h
+++ b/neuralnetworks/utils/common/test/MockBuffer.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER
+#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>
@@ -27,11 +27,11 @@
 class MockBuffer final : public IBuffer {
   public:
     MOCK_METHOD(Request::MemoryDomainToken, getToken, (), (const, override));
-    MOCK_METHOD(GeneralResult<void>, copyTo, (const Memory& dst), (const, override));
-    MOCK_METHOD(GeneralResult<void>, copyFrom, (const Memory& src, const Dimensions& dimensions),
-                (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
+#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
index 5566968..a9428bc 100644
--- a/neuralnetworks/utils/common/test/MockDevice.h
+++ b/neuralnetworks/utils/common/test/MockDevice.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE
+#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>
@@ -29,7 +29,6 @@
     MOCK_METHOD(const std::string&, getVersionString, (), (const, override));
     MOCK_METHOD(Version, getFeatureLevel, (), (const, override));
     MOCK_METHOD(DeviceType, getType, (), (const, override));
-    MOCK_METHOD(bool, isUpdatable, (), (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, (),
@@ -55,4 +54,4 @@
 
 }  // namespace android::nn
 
-#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/utils/common/test/MockPreparedModel.h b/neuralnetworks/utils/common/test/MockPreparedModel.h
index 418af61..c004861 100644
--- a/neuralnetworks/utils/common/test/MockPreparedModel.h
+++ b/neuralnetworks/utils/common/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL
+#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>
@@ -41,4 +41,4 @@
 
 }  // namespace android::nn
 
-#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL
+#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
index deb9b7c..7afd020 100644
--- a/neuralnetworks/utils/common/test/ResilientBufferTest.cpp
+++ b/neuralnetworks/utils/common/test/ResilientBufferTest.cpp
@@ -15,9 +15,11 @@
  */
 
 #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"
@@ -113,7 +115,8 @@
     EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(Return(kNoError));
 
     // run test
-    const auto result = buffer->copyTo({});
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyTo(memory);
 
     // verify result
     ASSERT_TRUE(result.has_value())
@@ -126,7 +129,8 @@
     EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(kReturnGeneralFailure);
 
     // run test
-    const auto result = buffer->copyTo({});
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyTo(memory);
 
     // verify result
     ASSERT_FALSE(result.has_value());
@@ -140,7 +144,8 @@
     EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
 
     // run test
-    const auto result = buffer->copyTo({});
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyTo(memory);
 
     // verify result
     ASSERT_FALSE(result.has_value());
@@ -156,7 +161,8 @@
     EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
 
     // run test
-    const auto result = buffer->copyTo({});
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyTo(memory);
 
     // verify result
     ASSERT_TRUE(result.has_value())
@@ -169,7 +175,8 @@
     EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(Return(kNoError));
 
     // run test
-    const auto result = buffer->copyFrom({}, {});
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyFrom(memory, {});
 
     // verify result
     ASSERT_TRUE(result.has_value())
@@ -182,7 +189,8 @@
     EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(kReturnGeneralFailure);
 
     // run test
-    const auto result = buffer->copyFrom({}, {});
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyFrom(memory, {});
 
     // verify result
     ASSERT_FALSE(result.has_value());
@@ -196,7 +204,8 @@
     EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
 
     // run test
-    const auto result = buffer->copyFrom({}, {});
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyFrom(memory, {});
 
     // verify result
     ASSERT_FALSE(result.has_value());
@@ -212,7 +221,8 @@
     EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
 
     // run test
-    const auto result = buffer->copyFrom({}, {});
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyFrom(memory, {});
 
     // verify result
     ASSERT_TRUE(result.has_value())
diff --git a/neuralnetworks/utils/service/Android.bp b/neuralnetworks/utils/service/Android.bp
index 402598c..5f36dff 100644
--- a/neuralnetworks/utils/service/Android.bp
+++ b/neuralnetworks/utils/service/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: "neuralnetworks_utils_hal_service",
     defaults: ["neuralnetworks_utils_defaults"],
@@ -26,12 +35,15 @@
         "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
index e339627..2fd5237 100644
--- a/neuralnetworks/utils/service/include/nnapi/hal/Service.h
+++ b/neuralnetworks/utils/service/include/nnapi/hal/Service.h
@@ -22,10 +22,15 @@
 #include <memory>
 #include <vector>
 
-namespace android::nn::hal {
+namespace android::hardware::neuralnetworks::service {
 
-std::vector<nn::SharedDevice> getDevices();
+struct SharedDeviceAndUpdatability {
+    nn::SharedDevice device;
+    bool isDeviceUpdatable = false;
+};
 
-}  // namespace android::nn::hal
+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
index a59549d..2286288 100644
--- a/neuralnetworks/utils/service/src/Service.cpp
+++ b/neuralnetworks/utils/service/src/Service.cpp
@@ -16,7 +16,10 @@
 
 #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>
@@ -31,6 +34,8 @@
 #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>
@@ -42,11 +47,12 @@
 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 getDevicesForVersion(const std::string& descriptor, getDeviceFn getDevice,
-                          std::vector<nn::SharedDevice>* devices,
-                          std::unordered_set<std::string>* registeredDevices) {
+void getHidlDevicesForVersion(const std::string& descriptor, getDeviceFn getDevice,
+                              std::vector<SharedDeviceAndUpdatability>* devices,
+                              std::unordered_set<std::string>* registeredDevices) {
     CHECK(devices != nullptr);
     CHECK(registeredDevices != nullptr);
 
@@ -57,7 +63,7 @@
             if (maybeDevice.has_value()) {
                 auto device = std::move(maybeDevice).value();
                 CHECK(device != nullptr);
-                devices->push_back(std::move(device));
+                devices->push_back({.device = std::move(device)});
             } else {
                 LOG(ERROR) << "getDevice(" << name << ") failed with " << maybeDevice.error().code
                            << ": " << maybeDevice.error().message;
@@ -66,29 +72,66 @@
     }
 }
 
-std::vector<nn::SharedDevice> getDevices() {
-    std::vector<nn::SharedDevice> devices;
+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;
 
-    getDevicesForVersion(V1_3::IDevice::descriptor, &V1_3::utils::getDevice, &devices,
-                         &registeredDevices);
-    getDevicesForVersion(V1_2::IDevice::descriptor, &V1_2::utils::getDevice, &devices,
-                         &registeredDevices);
-    getDevicesForVersion(V1_1::IDevice::descriptor, &V1_1::utils::getDevice, &devices,
-                         &registeredDevices);
-    getDevicesForVersion(V1_0::IDevice::descriptor, &V1_0::utils::getDevice, &devices,
-                         &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
 }  // namespace android::hardware::neuralnetworks::service
-
-namespace android::nn::hal {
-
-std::vector<nn::SharedDevice> getDevices() {
-    return hardware::neuralnetworks::service::getDevices();
-}
-
-}  // namespace android::nn::hal
diff --git a/nfc/1.0/Android.bp b/nfc/1.0/Android.bp
index 667922a..55c8639 100644
--- a/nfc/1.0/Android.bp
+++ b/nfc/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.nfc@1.0",
     root: "android.hardware",
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.1/Android.bp b/nfc/1.1/Android.bp
index a8976b0..a8463cf 100644
--- a/nfc/1.1/Android.bp
+++ b/nfc/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.nfc@1.1",
     root: "android.hardware",
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.2/Android.bp b/nfc/1.2/Android.bp
index 514d531..4831ab9 100644
--- a/nfc/1.2/Android.bp
+++ b/nfc/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.nfc@1.2",
     root: "android.hardware",
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/oemlock/1.0/Android.bp b/oemlock/1.0/Android.bp
index 8ab2911..da536c4 100644
--- a/oemlock/1.0/Android.bp
+++ b/oemlock/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.oemlock@1.0",
     root: "android.hardware",
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/aidl/Android.bp b/oemlock/aidl/Android.bp
index bfc99e7..439d43d 100644
--- a/oemlock/aidl/Android.bp
+++ b/oemlock/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.oemlock",
     vendor_available: true,
diff --git a/oemlock/aidl/default/Android.bp b/oemlock/aidl/default/Android.bp
index b9872d7..84136fe 100644
--- a/oemlock/aidl/default/Android.bp
+++ b/oemlock/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.oemlock-service.example",
     relative_install_path: "hw",
@@ -25,7 +34,7 @@
         "OemLock.cpp",
     ],
     shared_libs: [
-        "android.hardware.oemlock-ndk_platform",
+        "android.hardware.oemlock-V1-ndk_platform",
         "libbase",
         "libbinder_ndk",
     ],
diff --git a/oemlock/aidl/vts/Android.bp b/oemlock/aidl/vts/Android.bp
index a13dbe2..840d20a 100644
--- a/oemlock/aidl/vts/Android.bp
+++ b/oemlock/aidl/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 {
     name: "VtsHalOemLockTargetTest",
     defaults: [
@@ -25,7 +34,7 @@
         "libbinder_ndk",
         "libbase",
     ],
-    static_libs: ["android.hardware.oemlock-ndk_platform"],
+    static_libs: ["android.hardware.oemlock-V1-ndk_platform"],
     test_suites: [
         "general-tests",
         "vts",
diff --git a/power/1.0/Android.bp b/power/1.0/Android.bp
index 7381c70..68596ef 100644
--- a/power/1.0/Android.bp
+++ b/power/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.power@1.0",
     root: "android.hardware",
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.1/Android.bp b/power/1.1/Android.bp
index e026e70..259be42 100644
--- a/power/1.1/Android.bp
+++ b/power/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.power@1.1",
     root: "android.hardware",
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.2/Android.bp b/power/1.2/Android.bp
index ccf66dc..aad89e0 100644
--- a/power/1.2/Android.bp
+++ b/power/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.power@1.2",
     root: "android.hardware",
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.3/Android.bp b/power/1.3/Android.bp
index 15955e5..9346797 100644
--- a/power/1.3/Android.bp
+++ b/power/1.3/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.power@1.3",
     root: "android.hardware",
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/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/stats/1.0/Android.bp b/power/stats/1.0/Android.bp
index 2a71490..72da04e 100644
--- a/power/stats/1.0/Android.bp
+++ b/power/stats/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.power.stats@1.0",
     root: "android.hardware",
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/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/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/power/stats/aidl/android/hardware/power/stats/Channel.aidl
similarity index 62%
copy from security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
copy to power/stats/aidl/android/hardware/power/stats/Channel.aidl
index c3b402e..5e8962e 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
+++ b/power/stats/aidl/android/hardware/power/stats/Channel.aidl
@@ -14,13 +14,22 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
+package android.hardware.power.stats;
 
-/**
- * This is used to contain a byte[], to make out parameters of byte arrays
- * more convenient for callers.
- */
 @VintfStability
-parcelable ByteArray {
-    byte[] data;
+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/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/power/stats/aidl/android/hardware/power/stats/EnergyConsumerAttribution.aidl
similarity index 68%
copy from security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
copy to power/stats/aidl/android/hardware/power/stats/EnergyConsumerAttribution.aidl
index c3b402e..5767de1 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
+++ b/power/stats/aidl/android/hardware/power/stats/EnergyConsumerAttribution.aidl
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
+package android.hardware.power.stats;
 
-/**
- * This is used to contain a byte[], to make out parameters of byte arrays
- * more convenient for callers.
- */
 @VintfStability
-parcelable ByteArray {
-    byte[] data;
+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..12d2042
--- /dev/null
+++ b/power/stats/aidl/android/hardware/power/stats/EnergyConsumerResult.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.EnergyConsumerAttribution;
+
+@VintfStability
+parcelable EnergyConsumerResult {
+    /**
+     * ID of the EnergyConsumer associated with this result
+     */
+    int id;
+    /**
+     * Time since boot in milliseconds
+     */
+    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/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/power/stats/aidl/android/hardware/power/stats/EnergyConsumerType.aidl
similarity index 72%
copy from security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
copy to power/stats/aidl/android/hardware/power/stats/EnergyConsumerType.aidl
index c3b402e..d871ced 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
+++ b/power/stats/aidl/android/hardware/power/stats/EnergyConsumerType.aidl
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
+package android.hardware.power.stats;
 
-/**
- * This is used to contain a byte[], to make out parameters of byte arrays
- * more convenient for callers.
- */
+/* Indicates the type of an energy consumer reported by the Power Stats HAL */
 @VintfStability
-parcelable ByteArray {
-    byte[] data;
+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..d3e8f46
--- /dev/null
+++ b/power/stats/aidl/android/hardware/power/stats/EnergyMeasurement.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.
+ */
+
+package android.hardware.power.stats;
+
+@VintfStability
+parcelable EnergyMeasurement {
+    /**
+     * ID of the Channel associated with this measurement
+     */
+    int id;
+    /**
+     * Approximate time of data capture in millseconds since boot
+     */
+    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/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/power/stats/aidl/android/hardware/power/stats/PowerEntity.aidl
similarity index 61%
copy from security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
copy to power/stats/aidl/android/hardware/power/stats/PowerEntity.aidl
index c3b402e..6844a4c 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
+++ b/power/stats/aidl/android/hardware/power/stats/PowerEntity.aidl
@@ -14,13 +14,22 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
+package android.hardware.power.stats;
 
-/**
- * This is used to contain a byte[], to make out parameters of byte arrays
- * more convenient for callers.
- */
+import android.hardware.power.stats.State;
+
 @VintfStability
-parcelable ByteArray {
-    byte[] data;
-}
+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/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/power/stats/aidl/android/hardware/power/stats/State.aidl
similarity index 67%
copy from security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
copy to power/stats/aidl/android/hardware/power/stats/State.aidl
index c3b402e..33a9f70 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
+++ b/power/stats/aidl/android/hardware/power/stats/State.aidl
@@ -14,13 +14,18 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
+package android.hardware.power.stats;
 
-/**
- * This is used to contain a byte[], to make out parameters of byte arrays
- * more convenient for callers.
- */
 @VintfStability
-parcelable ByteArray {
-    byte[] data;
+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/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/power/stats/aidl/android/hardware/power/stats/StateResidencyResult.aidl
similarity index 65%
copy from security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
copy to power/stats/aidl/android/hardware/power/stats/StateResidencyResult.aidl
index c3b402e..949879c 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
+++ b/power/stats/aidl/android/hardware/power/stats/StateResidencyResult.aidl
@@ -14,13 +14,19 @@
  * limitations under the License.
  */
 
-package android.hardware.security.keymint;
+package android.hardware.power.stats;
 
-/**
- * This is used to contain a byte[], to make out parameters of byte arrays
- * more convenient for callers.
- */
+import android.hardware.power.stats.StateResidency;
+
 @VintfStability
-parcelable ByteArray {
-    byte[] data;
+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/radio/1.0/Android.bp b/radio/1.0/Android.bp
index f3cc2e0..cd64bca 100644
--- a/radio/1.0/Android.bp
+++ b/radio/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.radio@1.0",
     root: "android.hardware",
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/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/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..fc37201 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();
 }
@@ -99,4 +101,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 218e823..eeb1d29 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,
@@ -81,4 +97,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 b325015..abff05c 100644
--- a/radio/1.1/Android.bp
+++ b/radio/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.radio@1.1",
     root: "android.hardware",
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/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/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 0a4caf1..924c77a 100644
--- a/radio/1.2/Android.bp
+++ b/radio/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.radio@1.2",
     root: "android.hardware",
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/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/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 1c8e6c2..8d24b2b 100644
--- a/radio/1.3/Android.bp
+++ b/radio/1.3/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.radio@1.3",
     root: "android.hardware",
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.4/Android.bp b/radio/1.4/Android.bp
index 6c3a7d2..385f2d3 100644
--- a/radio/1.4/Android.bp
+++ b/radio/1.4/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.radio@1.4",
     root: "android.hardware",
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/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.5/Android.bp b/radio/1.5/Android.bp
index 74de0fd..a36f296 100644
--- a/radio/1.5/Android.bp
+++ b/radio/1.5/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.radio@1.5",
     root: "android.hardware",
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/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.6/Android.bp b/radio/1.6/Android.bp
index fc3191f..4b79c0a 100644
--- a/radio/1.6/Android.bp
+++ b/radio/1.6/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.radio@1.6",
     root: "android.hardware",
diff --git a/radio/1.6/IRadio.hal b/radio/1.6/IRadio.hal
index 3dc80b9..e2d35d0 100644
--- a/radio/1.6/IRadio.hal
+++ b/radio/1.6/IRadio.hal
@@ -18,9 +18,12 @@
 
 
 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;
@@ -100,6 +103,12 @@
      *   - 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.
@@ -117,15 +126,31 @@
      * @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()
      *
-     * Note this API is the same as the 1.5
      */
     oneway setupDataCall_1_6(int32_t serial, AccessNetwork accessNetwork,
             DataProfileInfo dataProfileInfo, bool roamingAllowed,
             DataRequestReason reason, vec<LinkAddress> addresses, vec<string> dnses,
-            int32_t pduSessionId);
+            int32_t pduSessionId, OptionalSliceInfo sliceInfo,
+            OptionalTrafficDescriptor trafficDescriptor, bool matchAllRuleAllowed);
 
     /**
      * Send an SMS message
@@ -151,7 +176,7 @@
      * @param serial Serial number of request.
      * @param message GsmSmsMessage as defined in types.hal
      *
-     * Response function is IRadioResponse.sendSMSExpectMoreResponse_1_6()
+     * Response function is IRadioResponse.sendSmsExpectMoreResponse_1_6()
      *
      * Note this API is the same as the 1.0
      *
@@ -159,7 +184,7 @@
      * 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);
+    oneway sendSmsExpectMore_1_6(int32_t serial, GsmSmsMessage message);
 
     /**
      * Send a CDMA SMS message
@@ -241,7 +266,7 @@
      * 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,
@@ -321,30 +346,32 @@
     /**
      * Requests to set the network type for searching and registering.
      *
-     * Instruct the radio to *only* accept the types of network provided. This
-     * is stronger than setPreferredNetworkType which is a suggestion.
+     * 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.setNetworkTypeBitmapResponse()
+     * Response callback is IRadioResponse.setAllowedNetworkTypesBitmapResponse()
      */
-    oneway setAllowedNetworkTypeBitmap(
+    oneway setAllowedNetworkTypesBitmap(
             uint32_t serial, bitfield<RadioAccessFamily> networkTypeBitmap);
 
     /**
      * Requests bitmap representing the currently allowed network types.
      *
-     * Requests the bitmap set by the corresponding method
-     * setAllowedNetworkTypeBitmap, which sets a strict set of RATs for the
-     * radio to use. Differs from getPreferredNetworkType and getPreferredNetworkTypeBitmap
-     * in that those request *preferences*.
+     * 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.getNetworkTypeBitmapResponse()
+     * Response callback is IRadioResponse.getAllowedNetworkTypesBitmapResponse()
      */
-    oneway getAllowedNetworkTypeBitmap(uint32_t serial);
+    oneway getAllowedNetworkTypesBitmap(int32_t serial);
 
     /**
      * Control data throttling at modem.
@@ -372,6 +399,64 @@
             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.
@@ -386,7 +471,7 @@
      * 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 identitcal to getCellInfoList in V1.0, but it requests updated version of CellInfo.
+     * This is identical to getCellInfoList in V1.0, but it requests updated version of CellInfo.
      *
      * @param serial Serial number of request.
      *
@@ -429,4 +514,72 @@
      * 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
index a53d7c1..05a7585 100644
--- a/radio/1.6/IRadioIndication.hal
+++ b/radio/1.6/IRadioIndication.hal
@@ -23,7 +23,9 @@
 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.
@@ -72,7 +74,6 @@
      */
     oneway currentLinkCapacityEstimate_1_6(RadioIndicationType type, LinkCapacityEstimate lce);
 
-
     /**
      * Indicates current signal strength of the radio.
      *
@@ -106,11 +107,34 @@
     /**
      * Indicates physical channel configurations.
      *
-     * An empty configs list indicates that the radio is in idle mode.
+     * 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
index 6ac86c3..a1286a5 100644
--- a/radio/1.6/IRadioResponse.hal
+++ b/radio/1.6/IRadioResponse.hal
@@ -19,12 +19,15 @@
 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.
@@ -136,7 +139,7 @@
      *   RadioError:ACCESS_BARRED
      *   RadioError:BLOCKED_DUE_TO_CALL
      */
-    oneway sendSMSExpectMoreResponse_1_6(RadioResponseInfo info, SendSmsResult sms);
+    oneway sendSmsExpectMoreResponse_1_6(RadioResponseInfo info, SendSmsResult sms);
 
     /**
      * @param info Response info struct containing response type, serial no. and error
@@ -228,6 +231,8 @@
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:INVALID_STATE
      */
     oneway setNrDualConnectivityStateResponse(RadioResponseInfo info);
 
@@ -240,6 +245,7 @@
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
+     *   RadioError:REQUEST_NOT_SUPPORTED
      */
     oneway isNrDualConnectivityEnabledResponse(RadioResponseInfo info, bool isEnabled);
 
@@ -296,7 +302,7 @@
     oneway cancelHandoverResponse(RadioResponseInfo info);
 
     /**
-     * Callback of IRadio.setAllowedNetworkTypeBitmap(int, bitfield<RadioAccessFamily>)
+     * Callback of IRadio.setAllowedNetworkTypesBitmap(int, bitfield<RadioAccessFamily>)
      *
      * Valid errors returned:
      *   RadioError:NONE
@@ -309,10 +315,12 @@
      *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:NO_RESOURCES
      */
-    oneway setAllowedNetworkTypeBitmapResponse(RadioResponseInfo info);
+    oneway setAllowedNetworkTypesBitmapResponse(RadioResponseInfo info);
 
     /**
-     * Callback of IRadio.getAllowedNetworkTypeBitmap(int, bitfield<RadioAccessFamily>)
+     * 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
@@ -325,7 +333,7 @@
      *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:NO_RESOURCES
      */
-    oneway getAllowedNetworkTypeBitmapResponse(
+    oneway getAllowedNetworkTypesBitmapResponse(
             RadioResponseInfo info, bitfield<RadioAccessFamily> networkTypeBitmap);
 
     /**
@@ -336,11 +344,13 @@
      *  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
@@ -348,7 +358,8 @@
      *   RadioError:INTERNAL_ERR
      *   RadioError:INVALID_ARGUMENTS
      */
-    oneway getSystemSelectionChannelsResponse(RadioResponseInfo info);
+    oneway getSystemSelectionChannelsResponse(
+            RadioResponseInfo info, vec<RadioAccessSpecifier> specifiers);
 
     /**
      * This is identical to getCellInfoListResponse_1_5 but uses an updated version of CellInfo.
@@ -414,4 +425,70 @@
      *   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
index 6dd8315..4b75db5 100644
--- a/radio/1.6/types.hal
+++ b/radio/1.6/types.hal
@@ -27,6 +27,7 @@
 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;
@@ -54,9 +55,9 @@
 
 struct QosBandwidth {
     /** Maximum bit rate possible on the bearer */
-    int32_t maxBitrateKbps;
+    uint32_t maxBitrateKbps;
     /** Minimum bit rate that is guaranteed to be provided by the network */
-    int32_t guaranteedBitrateKbps;
+    uint32_t guaranteedBitrateKbps;
 };
 
 /** LTE/EPS Quality of Service parameters as per 3gpp spec 24.301 sec 9.9.4.3. */
@@ -105,7 +106,7 @@
 /**
  * Next header protocol numbers defined by IANA, RFC 5237
  */
-enum QosProtocol : int32_t {
+enum QosProtocol : int8_t {
     /** No protocol specified */
     UNSPECIFIED = -1,
     /** Transmission Control Protocol */
@@ -118,14 +119,14 @@
     AH = 51,
 };
 
-enum QosFilterDirection : int32_t {
+enum QosFilterDirection : int8_t {
     DOWNLINK = 0,
     UPLINK = 1,
     BIDIRECTIONAL = 2,
 };
 
 /** Allowed port numbers */
-enum QosPortRange : int32_t {
+enum QosPortRange : uint16_t {
     MIN = 20,
     MAX = 65535
 };
@@ -247,7 +248,7 @@
 };
 
 /** The allowed failure modes on an IWLAN handover failure. */
-enum HandoverFailureMode : int32_t {
+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.
@@ -273,7 +274,8 @@
 /**
  * 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, and handoverFailureMode in this version.
+ * Also added defaultQos, qosSessions, handoverFailureMode, pduSessionId, sliceInfo,
+ * and traffic descriptors in this version.
  */
 struct SetupDataCallResult {
     /** Data call fail cause. DataCallFailCause.NONE if no error. */
@@ -284,6 +286,9 @@
      * 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;
 
@@ -356,12 +361,25 @@
      * 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: int32_t {
+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.
@@ -390,7 +408,7 @@
     * 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 -1 if network is not connected.
+    * This must be filled with 0 if network is not connected.
     */
    uint32_t downlinkCapacityKbps;
 
@@ -400,7 +418,7 @@
     * 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 -1 if network is not connected.
+    * This must be filled with 0 if network is not connected.
     */
    uint32_t uplinkCapacityKbps;
 
@@ -409,7 +427,8 @@
     * 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 -1 if secondary is not connected.
+    * mode. This must be filled with 0 if secondary is not connected or if
+    * modem does not support this feature.
     */
    uint32_t secondaryDownlinkCapacityKbps;
 
@@ -418,12 +437,13 @@
     * 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 -1 if secondary is not 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 : int32_t {
+enum DataThrottlingAction : int8_t {
     /* Clear all existing data throttling. */
     NO_DATA_THROTTLING = 0,
 
@@ -561,9 +581,9 @@
      *
      * Reference: 3GPP TS 138.214 section 5.2.2.1.
      *
-     * Range [0, 15], INT_MAX means invalid/unreported.
+     * Range [0, 15], 0xFF means invalid/unreported.
      */
-    vec<uint32_t> csiCqiReport;
+    vec<uint8_t> csiCqiReport;
 };
 
 /**
@@ -687,8 +707,10 @@
     RegState regState;
 
     /**
-     * Indicates the available voice radio technology, valid values as
-     * defined by RadioTechnology.
+     * 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;
 
@@ -718,14 +740,19 @@
 
         EutranRegistrationInfo eutranInfo;
 
-        struct NgranRegistrationInfo {
-            /**
-             * 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 nrVopsInfo;
-        } ngranInfo;
+        /**
+         * 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;
 };
 
@@ -752,10 +779,10 @@
     int32_t uplinkChannelNumber;
 
     /** Downlink cell bandwidth, in kHz */
-    int32_t cellBandwidthDownlink;
+    int32_t cellBandwidthDownlinkKhz;
 
     /** Uplink cell bandwidth, in kHz */
-    int32_t cellBandwidthUplink;
+    int32_t cellBandwidthUplinkKhz;
 
     /**
      * A list of data calls mapped to this physical channel. The context id must match the cid of
@@ -804,3 +831,378 @@
     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/functional/Android.bp b/radio/1.6/vts/functional/Android.bp
index db90f88..2bc6af3 100644
--- a/radio/1.6/vts/functional/Android.bp
+++ b/radio/1.6/vts/functional/Android.bp
@@ -14,11 +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: "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",
@@ -27,6 +37,7 @@
     ],
     static_libs: [
         "RadioVtsTestUtilBase",
+        "RadioConfigVtsTestResponse",
         "android.hardware.radio@1.6",
         "android.hardware.radio@1.5",
         "android.hardware.radio@1.4",
@@ -36,8 +47,13 @@
         "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"],
+    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/radio_hidl_hal_api.cpp b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
index 47babed..7c05984 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
@@ -56,8 +56,18 @@
     ::android::hardware::radio::V1_2::DataRequestReason reason =
             ::android::hardware::radio::V1_2::DataRequestReason::NORMAL;
 
-    Return<void> res = radio_v1_6->setupDataCall_1_6(serial, accessNetwork, dataProfileInfo,
-                                                     roamingAllowed, reason, addresses, dnses, -1);
+    ::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());
@@ -78,6 +88,93 @@
     }
 }
 
+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.
  */
@@ -107,10 +204,10 @@
 }
 
 /*
- * Test IRadio_1_6.sendSMSExpectMore() for the response returned.
+ * Test IRadio_1_6.sendSmsExpectMore() for the response returned.
  */
-TEST_P(RadioHidlTest_v1_6, sendSMSExpectMore_1_6) {
-    LOG(DEBUG) << "sendSMSExpectMore";
+TEST_P(RadioHidlTest_v1_6, sendSmsExpectMore_1_6) {
+    LOG(DEBUG) << "sendSmsExpectMore";
     serial = GetRandomSerialNumber();
     GsmSmsMessage msg;
     msg.smscPdu = "";
@@ -130,7 +227,7 @@
              ::android::hardware::radio::V1_6::RadioError::SIM_ABSENT},
             CHECK_GENERAL_ERROR));
     }
-    LOG(DEBUG) << "sendSMSExpectMore finished";
+    LOG(DEBUG) << "sendSmsExpectMore finished";
 }
 
 /*
@@ -272,10 +369,18 @@
     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,
+    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}));
+    }
 }
 
 /*
@@ -290,10 +395,17 @@
     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,
+    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}));
+    }
 }
 
 /*
@@ -309,13 +421,20 @@
     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::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}));
+    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,
@@ -324,13 +443,20 @@
     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::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}));
+    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);
@@ -339,13 +465,20 @@
     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::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}));
+    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);
@@ -353,12 +486,18 @@
     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::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}));
+    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}));
+    }
 }
 
 /*
@@ -408,6 +547,167 @@
 }
 
 /*
+ * 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(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(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(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) {
@@ -418,3 +718,167 @@
     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_F(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);
+        EXPECT_EQ(::android::hardware::radio::V1_6::RadioError::NONE, radioRsp_v1_6->rspInfo.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..4222441
--- /dev/null
+++ b/radio/1.6/vts/functional/radio_hidl_hal_misc.cpp
@@ -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.
+ */
+
+#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) {
+            if (info.operatorNumeric != nullptr) {
+                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
index 79c3cde..5d514a0 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_test.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_test.cpp
@@ -45,33 +45,27 @@
     EXPECT_EQ(CardState::PRESENT, cardStatus.base.base.base.cardState);
 }
 
-/*
- * Notify that the response message is received.
- */
-void RadioHidlTest_v1_6::notify(int receivedSerial) {
-    std::unique_lock<std::mutex> lock(mtx_);
-    if (serial == receivedSerial) {
-        count_++;
-        cv_.notify_one();
-    }
-}
+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());
 
-/*
- * Wait till the response message is notified or till TIMEOUT_PERIOD.
- */
-std::cv_status RadioHidlTest_v1_6::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(TIMEOUT_PERIOD));
-        if (status == std::cv_status::timeout) {
-            return status;
-        }
+    // 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);
     }
-    count_--;
-    return status;
+
+    // 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() {
@@ -85,3 +79,29 @@
     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
index 5fcfa3b..3185f98 100644
--- 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
@@ -18,16 +18,12 @@
 
 #include <android-base/logging.h>
 
-#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
-#include <utils/Log.h>
+#include "radio_config_hidl_hal_utils.h"
+
 #include <chrono>
 #include <condition_variable>
 #include <mutex>
 
-#include <android/hardware/radio/config/1.1/IRadioConfig.h>
-
 #include <android/hardware/radio/1.6/IRadio.h>
 #include <android/hardware/radio/1.6/IRadioIndication.h>
 #include <android/hardware/radio/1.6/IRadioResponse.h>
@@ -49,7 +45,6 @@
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 
-#define TIMEOUT_PERIOD 75
 #define MODEM_EMERGENCY_CALL_ESTABLISH_TIME 3
 #define MODEM_EMERGENCY_CALL_DISCONNECT_TIME 3
 
@@ -61,16 +56,18 @@
 /* Callback class for radio response v1_6 */
 class RadioResponse_v1_6 : public ::android::hardware::radio::V1_6::IRadioResponse {
   protected:
-    RadioHidlTest_v1_6& parent_v1_6;
+    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_2::Call> currentCalls;
+    hidl_vec<::android::hardware::radio::V1_6::Call> currentCalls;
+    ::android::hardware::radio::V1_2::VoiceRegStateResult voiceRegResp;
 
     // Sms
     SendSmsResult sendSmsResult;
@@ -88,6 +85,7 @@
 
     // Data
     ::android::hardware::radio::V1_4::DataRegStateResult dataRegResp;
+    ::android::hardware::radio::V1_6::SetupDataCallResult setupDataCallResult;
 
     // SimLock status
     ::android::hardware::radio::V1_4::CarrierRestrictionsWithPriority carrierRestrictionsResp;
@@ -103,7 +101,12 @@
     ::android::hardware::radio::V1_5::CellIdentity barringCellIdentity;
     ::android::hardware::hidl_vec<::android::hardware::radio::V1_5::BarringInfo> barringInfos;
 
-    RadioResponse_v1_6(RadioHidlTest_v1_6& parent_v1_6);
+    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(
@@ -758,7 +761,7 @@
             const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
             const SendSmsResult& sms);
 
-    Return<void> sendSMSExpectMoreResponse_1_6(
+    Return<void> sendSmsExpectMoreResponse_1_6(
             const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
             const SendSmsResult& sms);
 
@@ -790,10 +793,10 @@
     Return<void> cancelHandoverResponse(
             const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
 
-    Return<void> setAllowedNetworkTypeBitmapResponse(
+    Return<void> setAllowedNetworkTypesBitmapResponse(
             const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
 
-    Return<void> getAllowedNetworkTypeBitmapResponse(
+    Return<void> getAllowedNetworkTypesBitmapResponse(
             const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
             const ::android::hardware::hidl_bitfield<
                     ::android::hardware::radio::V1_4::RadioAccessFamily>
@@ -803,7 +806,8 @@
             const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
 
     Return<void> getSystemSelectionChannelsResponse(
-            const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+            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,
@@ -825,6 +829,21 @@
     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 */
@@ -1069,18 +1088,20 @@
             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> {
+class RadioHidlTest_v1_6 : public ::testing::TestWithParam<std::string>,
+                           public RadioResponseWaiter {
   protected:
-    std::mutex mtx_;
-    std::condition_variable cv_;
-    int count_;
-
-    /* Serial number for radio request */
-    int serial;
-
     /* Clear Potential Established Calls */
     void clearPotentialEstablishedCalls();
 
@@ -1093,11 +1114,7 @@
   public:
     virtual void SetUp() override;
 
-    /* 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();
+    bool getRadioHalCapabilities();
 
     /* radio service handle */
     sp<::android::hardware::radio::V1_6::IRadio> radio_v1_6;
diff --git a/radio/1.6/vts/functional/radio_indication.cpp b/radio/1.6/vts/functional/radio_indication.cpp
index e7a9680..8292131 100644
--- a/radio/1.6/vts/functional/radio_indication.cpp
+++ b/radio/1.6/vts/functional/radio_indication.cpp
@@ -412,3 +412,16 @@
                 ::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
index 7c5cf6d..6e7b86f 100644
--- a/radio/1.6/vts/functional/radio_response.cpp
+++ b/radio/1.6/vts/functional/radio_response.cpp
@@ -18,7 +18,7 @@
 
 ::android::hardware::radio::V1_5::CardStatus cardStatus;
 
-RadioResponse_v1_6::RadioResponse_v1_6(RadioHidlTest_v1_6& parent) : parent_v1_6(parent) {}
+RadioResponse_v1_6::RadioResponse_v1_6(RadioResponseWaiter& parent) : parent_v1_6(parent) {}
 
 /* 1.0 Apis */
 Return<void> RadioResponse_v1_6::getIccCardStatusResponse(
@@ -87,7 +87,9 @@
 }
 
 Return<void> RadioResponse_v1_6::hangupConnectionResponse(
-        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& info) {
+    rspInfo_v1_0 = info;
+    parent_v1_6.notify(info.serial);
     return Void();
 }
 
@@ -272,8 +274,11 @@
 }
 
 Return<void> RadioResponse_v1_6::getAvailableNetworksResponse(
-        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
-        const ::android::hardware::hidl_vec<OperatorInfo>& /*networkInfos*/) {
+        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();
 }
 
@@ -749,7 +754,9 @@
 
 /* 1.1 Apis */
 Return<void> RadioResponse_v1_6::setCarrierInfoForImsiEncryptionResponse(
-        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& info) {
+    rspInfo_v1_0 = info;
+    parent_v1_6.notify(info.serial);
     return Void();
 }
 
@@ -816,8 +823,11 @@
 }
 
 Return<void> RadioResponse_v1_6::getVoiceRegistrationStateResponse_1_2(
-        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
-        const ::android::hardware::radio::V1_2::VoiceRegStateResult& /*voiceRegResponse*/) {
+        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();
 }
 
@@ -846,7 +856,9 @@
 
 /* 1.4 Apis */
 Return<void> RadioResponse_v1_6::emergencyDialResponse(
-        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& info) {
+    rspInfo_v1_0 = info;
+    parent_v1_6.notify(info.serial);
     return Void();
 }
 
@@ -1050,11 +1062,13 @@
 
 Return<void> RadioResponse_v1_6::setupDataCallResponse_1_6(
         const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
-        const android::hardware::radio::V1_6::SetupDataCallResult& /* dcResponse */) {
+        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;
@@ -1079,7 +1093,7 @@
     return Void();
 }
 
-Return<void> RadioResponse_v1_6::sendSMSExpectMoreResponse_1_6(
+Return<void> RadioResponse_v1_6::sendSmsExpectMoreResponse_1_6(
         const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
         const SendSmsResult& sms) {
     rspInfo = info;
@@ -1150,14 +1164,14 @@
     return Void();
 }
 
-Return<void> RadioResponse_v1_6::setAllowedNetworkTypeBitmapResponse(
+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::getAllowedNetworkTypeBitmapResponse(
+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>
@@ -1186,7 +1200,8 @@
 }
 
 Return<void> RadioResponse_v1_6::getSystemSelectionChannelsResponse(
-        const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
+        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();
@@ -1210,8 +1225,42 @@
 
 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*/) {
+        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/config/1.0/Android.bp b/radio/config/1.0/Android.bp
index eea4c34..9e317b3 100644
--- a/radio/config/1.0/Android.bp
+++ b/radio/config/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.radio.config@1.0",
     root: "android.hardware",
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.1/Android.bp b/radio/config/1.1/Android.bp
index 69d9a83..b1705f9 100644
--- a/radio/config/1.1/Android.bp
+++ b/radio/config/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.radio.config@1.1",
     root: "android.hardware",
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.2/Android.bp b/radio/config/1.2/Android.bp
index f751868..3327af4 100644
--- a/radio/config/1.2/Android.bp
+++ b/radio/config/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.radio.config@1.2",
     root: "android.hardware",
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.3/Android.bp b/radio/config/1.3/Android.bp
index ace0de9..dc0d82c 100644
--- a/radio/config/1.3/Android.bp
+++ b/radio/config/1.3/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.radio.config@1.3",
     root: "android.hardware",
     srcs: [
-        "types.hal",
         "IRadioConfig.hal",
         "IRadioConfigResponse.hal",
     ],
diff --git a/radio/config/1.3/IRadioConfigResponse.hal b/radio/config/1.3/IRadioConfigResponse.hal
index 863754f..f6aee31 100644
--- a/radio/config/1.3/IRadioConfigResponse.hal
+++ b/radio/config/1.3/IRadioConfigResponse.hal
@@ -18,7 +18,6 @@
 
 import android.hardware.radio@1.6::RadioResponseInfo;
 import @1.2::IRadioConfigResponse;
-import HalDeviceCapabilities;
 
 /**
  * Interface declaring response functions to solicited radio config requests.
@@ -26,8 +25,20 @@
 interface IRadioConfigResponse extends @1.2::IRadioConfigResponse {
     /**
      * @param info Response info struct containing response type, serial no. and error
-     * @param capabilities Capabilities struct containing the capabilities of the
-     * device related to the Radio HAL
+     * @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
@@ -35,5 +46,5 @@
      *   RadioError:INTERNAL_ERR
      */
     oneway getHalDeviceCapabilitiesResponse(RadioResponseInfo info,
-        HalDeviceCapabilities capabilities);
+        bool modemReducedFeatureSet1);
 };
diff --git a/radio/config/1.3/types.hal b/radio/config/1.3/types.hal
index bedb709..2b6c9f0 100644
--- a/radio/config/1.3/types.hal
+++ b/radio/config/1.3/types.hal
@@ -19,4 +19,21 @@
 /**
  * Contains the device capabilities with respect to the Radio HAL.
  */
-struct HalDeviceCapabilities {};
+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
index abd081f..20c480f 100644
--- a/radio/config/1.3/vts/functional/Android.bp
+++ b/radio/config/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: "VtsHalRadioConfigV1_3TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -37,3 +46,26 @@
         "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/radio_config_hidl_hal_test.cpp b/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
index de8365a..da61464 100644
--- 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
@@ -31,32 +31,3 @@
 
     radioConfig->setResponseFunctions(radioConfigRsp, nullptr);
 }
-
-/*
- * Notify that the response message is received.
- */
-void RadioConfigHidlTest::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 TIMEOUT_PERIOD.
- */
-std::cv_status RadioConfigHidlTest::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(TIMEOUT_PERIOD));
-        if (status == std::cv_status::timeout) {
-            return status;
-        }
-    }
-    count_--;
-    return status;
-}
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
index 439eb70..7440054 100644
--- 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
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#pragma once
+
 #include <android-base/logging.h>
 
 #include <chrono>
@@ -27,7 +29,6 @@
 #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 <android/hardware/radio/config/1.3/types.h>
 #include <gtest/gtest.h>
 #include <hidl/GtestPrinter.h>
 #include <hidl/ServiceManagement.h>
@@ -45,11 +46,9 @@
 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::HalDeviceCapabilities;
 using ::android::hardware::radio::config::V1_3::IRadioConfig;
 using ::android::hardware::radio::V1_0::RadioResponseInfo;
 
-#define TIMEOUT_PERIOD 75
 #define RADIO_SERVICE_NAME "slot1"
 
 class RadioConfigHidlTest;
@@ -57,13 +56,14 @@
 /* Callback class for radio config response */
 class RadioConfigResponse : public IRadioConfigResponse {
   protected:
-    RadioConfigHidlTest& parent;
+    RadioResponseWaiter& parent;
 
   public:
     RadioResponseInfo rspInfo;
     PhoneCapability phoneCap;
+    bool modemReducedFeatureSet1;
 
-    RadioConfigResponse(RadioConfigHidlTest& parent);
+    RadioConfigResponse(RadioResponseWaiter& parent);
     virtual ~RadioConfigResponse() = default;
 
     Return<void> getSimSlotsStatusResponse(
@@ -89,7 +89,7 @@
 
     Return<void> getHalDeviceCapabilitiesResponse(
             const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
-            const HalDeviceCapabilities& halDeviceCapabilities);
+            bool modemReducedFeatureSet1);
 };
 
 /* Callback class for radio config indication */
@@ -107,26 +107,13 @@
 };
 
 // The main test class for Radio config HIDL.
-class RadioConfigHidlTest : public ::testing::TestWithParam<std::string> {
-  protected:
-    std::mutex mtx_;
-    std::condition_variable cv_;
-    int count_;
-
+class RadioConfigHidlTest : public ::testing::TestWithParam<std::string>,
+                            public RadioResponseWaiter {
   public:
     virtual void SetUp() override;
 
-    /* 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();
-
     void updateSimCardStatus();
 
-    /* Serial number for radio request */
-    int serial;
-
     /* radio config service handle */
     sp<IRadioConfig> radioConfig;
 
diff --git a/radio/config/1.3/vts/functional/radio_config_response.cpp b/radio/config/1.3/vts/functional/radio_config_response.cpp
index 2a8b28b..6b2d27c 100644
--- a/radio/config/1.3/vts/functional/radio_config_response.cpp
+++ b/radio/config/1.3/vts/functional/radio_config_response.cpp
@@ -18,7 +18,7 @@
 
 // SimSlotStatus slotStatus;
 
-RadioConfigResponse::RadioConfigResponse(RadioConfigHidlTest& parent) : parent(parent) {}
+RadioConfigResponse::RadioConfigResponse(RadioResponseWaiter& parent) : parent(parent) {}
 
 Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
         const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */,
@@ -65,6 +65,7 @@
 
 Return<void> RadioConfigResponse::getHalDeviceCapabilitiesResponse(
         const ::android::hardware::radio::V1_6::RadioResponseInfo& /* info */,
-        const ::android::hardware::radio::config::V1_3::HalDeviceCapabilities& /* capabilities */) {
+        bool modemReducedFeatures) {
+    modemReducedFeatureSet1 = modemReducedFeatures;
     return Void();
-}
\ No newline at end of file
+}
diff --git a/radio/deprecated/1.0/Android.bp b/radio/deprecated/1.0/Android.bp
index 1a7cb94..53f6da5 100644
--- a/radio/deprecated/1.0/Android.bp
+++ b/radio/deprecated/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.radio.deprecated@1.0",
     root: "android.hardware",
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/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/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/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 a32b9d1..520d3a3 100644
--- a/secure_element/1.0/Android.bp
+++ b/secure_element/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.secure_element@1.0",
     root: "android.hardware",
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.1/Android.bp b/secure_element/1.1/Android.bp
index 08e6c88..3874fda 100644
--- a/secure_element/1.1/Android.bp
+++ b/secure_element/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.secure_element@1.1",
     root: "android.hardware",
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.2/Android.bp b/secure_element/1.2/Android.bp
index 03df5f9..6de92df 100644
--- a/secure_element/1.2/Android.bp
+++ b/secure_element/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.secure_element@1.2",
     root: "android.hardware",
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/security/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp
index 5652827..0c11f3b 100644
--- a/security/keymint/aidl/Android.bp
+++ b/security/keymint/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.security.keymint",
     vendor_available: true,
@@ -10,12 +19,14 @@
     stability: "vintf",
     backend: {
         java: {
-            sdk_version: "module_current",
+            platform_apis: true,
+            srcs_available: true,
         },
         ndk: {
             vndk: {
                 enabled: true,
             },
+            apps_enabled: false,
         },
         rust: {
             enabled: true,
diff --git a/security/keymint/aidl/OWNERS b/security/keymint/aidl/OWNERS
index 5c79db8..a93b171 100644
--- a/security/keymint/aidl/OWNERS
+++ b/security/keymint/aidl/OWNERS
@@ -1,3 +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
index a6c3e65..6da124f 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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,
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
index 84395af..c952a31 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.security.keymint;
+/* @hide */
 @VintfStability
 parcelable BeginResult {
   long challenge;
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
index e914823..0049883 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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,
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
index 2277831..645f0a7 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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/ByteArray.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/DeviceInfo.aidl
similarity index 62%
copy from security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl
copy to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/DeviceInfo.aidl
index cef8eca..d04d49c 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/DeviceInfo.aidl
@@ -1,3 +1,18 @@
+/*
+ * 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,7 +32,8 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.security.keymint;
+/* @hide */
 @VintfStability
-parcelable ByteArray {
-  byte[] data;
+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
index 2e583ce..0df7096 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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,
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
index b372822..6b4a9ae 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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,
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
index aa8c071..b05a0f3 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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,
@@ -97,6 +113,12 @@
   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
index 5858c77..2e07924 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,12 +32,13 @@
 // 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 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
index 9ab00c1..dfc98f0 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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,
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
index c95145d..fa643fc 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,17 +32,22 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.security.keymint;
-@VintfStability
+/* @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);
-  android.hardware.security.keymint.KeyCreationResult importKey(in android.hardware.security.keymint.KeyParameter[] keyParams, in android.hardware.security.keymint.KeyFormat keyFormat, in byte[] keyData);
+  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[] inKeyBlobToUpgrade, in android.hardware.security.keymint.KeyParameter[] inUpgradeParams);
-  void deleteKey(in byte[] inKeyBlob);
+  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 inPurpose, in byte[] inKeyBlob, in android.hardware.security.keymint.KeyParameter[] inParams, in android.hardware.security.keymint.HardwareAuthToken inAuthToken);
+  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
index e6ab4c8..4ab4ffe 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,9 +32,11 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.security.keymint;
-@VintfStability
+/* @hide */
+@SensitiveData @VintfStability
 interface IKeyMintOperation {
-  int update(in @nullable android.hardware.security.keymint.KeyParameterArray inParams, in @nullable byte[] input, in @nullable android.hardware.security.keymint.HardwareAuthToken inAuthToken, in @nullable android.hardware.security.secureclock.TimeStampToken inTimeStampToken, out @nullable android.hardware.security.keymint.KeyParameterArray outParams, out @nullable android.hardware.security.keymint.ByteArray output);
-  byte[] finish(in @nullable android.hardware.security.keymint.KeyParameterArray inParams, in @nullable byte[] input, in @nullable byte[] inSignature, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken, in @nullable android.hardware.security.secureclock.TimeStampToken inTimeStampToken, out @nullable android.hardware.security.keymint.KeyParameterArray outParams);
+  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
index 49ea8af..008381f 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,8 +32,9 @@
 // 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 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
index 4b9ac79..9f77d3e 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.security.keymint;
+/* @hide */
 @VintfStability
 parcelable KeyCreationResult {
   byte[] keyBlob;
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
index 4eb5a78..9560d8d 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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,
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
index 0390ec9..2113e42a 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,10 +32,12 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.security.keymint;
-@VintfStability
+/* @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 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
index e84cf74..4b3c659 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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,
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
index 6829a2b..c5a1e01 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,8 +32,9 @@
 // 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 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/KeyParameterArray.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterArray.aidl
deleted file mode 100644
index 882ca89..0000000
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterArray.aidl
+++ /dev/null
@@ -1,23 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// 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;
-@VintfStability
-parcelable KeyParameterArray {
-  android.hardware.security.keymint.KeyParameter[] params;
-}
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
index 6c11a92..7a0b074 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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;
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
index ff8d85a..b84bec1 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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,
@@ -24,4 +40,6 @@
   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/ByteArray.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/MacedPublicKey.aidl
similarity index 62%
copy from security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl
copy to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/MacedPublicKey.aidl
index cef8eca..8095e8c 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/MacedPublicKey.aidl
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,7 +32,8 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.security.keymint;
+/* @hide */
 @VintfStability
-parcelable ByteArray {
-  byte[] data;
+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
index 6c61312..dba4a8a 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ProtectedData.aidl
similarity index 62%
rename from security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ProtectedData.aidl
index cef8eca..d1610b4 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ProtectedData.aidl
@@ -1,3 +1,18 @@
+/*
+ * 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,7 +32,8 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.security.keymint;
+/* @hide */
 @VintfStability
-parcelable ByteArray {
-  byte[] data;
+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
index c4812ed..0d278e0 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,9 +32,11 @@
 // 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
index ce12fed..e310b44 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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,
@@ -32,7 +48,6 @@
   RSA_PUBLIC_EXPONENT = 1342177480,
   INCLUDE_UNIQUE_ID = 1879048394,
   RSA_OAEP_MGF_DIGEST = 536871115,
-  BLOB_USAGE_REQUIREMENTS = 268435757,
   BOOTLOADER_ONLY = 1879048494,
   ROLLBACK_RESISTANCE = 1879048495,
   HARDWARE_TYPE = 268435760,
@@ -42,6 +57,7 @@
   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,
@@ -79,4 +95,9 @@
   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
index 41c8832..a7d1de5 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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,
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl b/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl
index 8300b0d..1820893 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl
@@ -18,6 +18,7 @@
 
 /**
  * Algorithms provided by IKeyMintDevice implementations.
+ * @hide
  */
 @VintfStability
 @Backing(type="int")
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
index aaf9f3c..b5336b9 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
@@ -21,10 +21,14 @@
 
 /**
  * This is all the results returned by the IKeyMintDevice begin() function.
+ * @hide
  */
 @VintfStability
 parcelable BeginResult {
-    /* This is the challenge used in verifyAuthorization.  It must be a nonce. */
+    /**
+     * This is the challenge used to verify authorization of an operation.
+     * See IKeyMintOperation.aidl entrypoints updateAad() and update().
+     */
     long challenge;
 
     /**
diff --git a/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl b/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl
index 629c89f..749da81 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl
@@ -18,6 +18,7 @@
 
 /**
  * Symmetric block cipher modes provided by IKeyMintDevice implementations.
+ * @hide
  */
 @VintfStability
 @Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl b/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl
index 0e5d898..21dfdd5 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl
@@ -18,6 +18,7 @@
 
 /**
  * This encodes an IKeyMintDevice certificate, generated for a KeyMint asymmetric public key.
+ * @hide
  */
 @VintfStability
 parcelable Certificate {
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..3ea14a1
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl
@@ -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 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
+     *         ? "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
+     *     }
+     */
+    byte[] deviceInfo;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Digest.aidl b/security/keymint/aidl/android/hardware/security/keymint/Digest.aidl
index b44da5a..a8768c3 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Digest.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Digest.aidl
@@ -18,6 +18,7 @@
 
 /**
  * Digests provided by keyMint implementations.
+ * @hide
  */
 @VintfStability
 @Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl b/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl
index b9d1646..5b1c10c 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl
@@ -18,6 +18,7 @@
 
 /**
  * Supported EC curves, used in ECDSA
+ * @hide
  */
 @VintfStability
 @Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
index b20601d..137e6b6 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
@@ -19,6 +19,7 @@
 /**
  * KeyMint error codes.  Aidl will return these error codes as service specific
  * errors in EX_SERVICE_SPECIFIC.
+ * @hide
  */
 @VintfStability
 @Backing(type="int")
@@ -42,7 +43,7 @@
     INVALID_AUTHORIZATION_TIMEOUT = -16,
     UNSUPPORTED_KEY_FORMAT = -17,
     INCOMPATIBLE_KEY_FORMAT = -18,
-    UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = -19,   /** For PKCS8 & PKCS12 */
+    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,
@@ -101,6 +102,12 @@
     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,
diff --git a/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
index 417a0b1..0933bd5 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
@@ -16,17 +16,18 @@
 
 package android.hardware.security.keymint;
 
-import android.hardware.security.secureclock.Timestamp;
 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.
+ * 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)
@@ -55,7 +56,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/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
index 33f71b8..2d9d0ff 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
@@ -20,6 +20,7 @@
  * 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")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index 1ca7713..c6f89bd 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -16,16 +16,18 @@
 
 package android.hardware.security.keymint;
 
+import android.hardware.security.keymint.AttestationKey;
 import android.hardware.security.keymint.BeginResult;
-import android.hardware.security.keymint.ByteArray;
 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.KeyParameter;
 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.
@@ -106,7 +108,6 @@
  *
  *      - 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.
@@ -149,8 +150,8 @@
  *
  * The IKeyMintDevice must ignore unknown tags.
  *
- * The caller must always provide the current date time in the keyParameter CREATION_DATETIME
- * 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
@@ -184,7 +185,7 @@
  * 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 encryp the private/secret key material.
+ * 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
@@ -210,8 +211,10 @@
  * 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;
 
@@ -244,7 +247,7 @@
      * 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 envionment's
+     * 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.
@@ -254,9 +257,6 @@
      *
      * o Tag::ORIGIN with the value KeyOrigin::GENERATED.
      *
-     * o Tag::BLOB_USAGE_REQUIREMENTS with the appropriate value (see KeyBlobUsageRequirements in
-     *   Tag.aidl).
-     *
      * o Tag::OS_VERSION, Tag::OS_PATCHLEVEL, Tag::VENDOR_PATCHLEVEL and Tag::BOOT_PATCHLEVEL with
      *   appropriate values.
      *
@@ -268,7 +268,7 @@
      *
      * 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
+     * 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.
@@ -285,7 +285,7 @@
      *   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 implementatiosn must support all Digest values (see digest.aidl) for RSA
+     *   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
@@ -295,11 +295,9 @@
      *
      * == ECDSA Keys ==
      *
-     * Either Tag::KEY_SIZE or Tag::EC_CURVE must be provided to generate an ECDSA key.  If neither
-     * is provided, generateKey must return ErrorCode::UNSUPPORTED_KEY_SIZE.  If Tag::KEY_SIZE is
-     * provided, the possible values are 224, 256, 384 and 521, and must be mapped to Tag::EC_CURVE
-     * values P_224, P_256, P_384 and P_521, respectively.  TEE IKeyMintDevice implementations
-     * must support all curves.  StrongBox implementations must support P_256.
+     * 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.
      *
      * == AES Keys ==
      *
@@ -309,14 +307,31 @@
      * 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::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 generateKey(in KeyParameter[] keyParams);
+    KeyCreationResult generateKey(
+            in KeyParameter[] keyParams, in @nullable AttestationKey attestationKey);
 
     /**
      * Imports key material into an IKeyMintDevice.  Key definition parameters and return values
@@ -336,26 +351,36 @@
      *
      * o Tag::ORIGIN (returned in keyCharacteristics) must have the value KeyOrigin::IMPORTED.
      *
-     * @param inKeyParams Key generation parameters are defined as KeyMintDevice tag/value pairs,
+     * @param keyParams Key generation parameters are defined as KeyMintDevice tag/value pairs,
      *        provided in params.
      *
-     * @param inKeyFormat The format of the key material to import.  See KeyFormat in
-     *        keyformat.aidl.
+     * @param keyFormat The format of the key material to import.  See KeyFormat in keyformat.aidl.
      *
-     * @param inKeyData The key material to import, in the format specified in keyFormat.
+     * @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 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 inWrappedKeyData The wrapped key material to import.
-     *     TODO(seleneh) Decide if we want the wrapped key in DER-encoded ASN.1 format or CBOR
-     *     format or both.  And specify the standarized format.
+     * @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.
@@ -373,20 +398,20 @@
      *
      *     Where:
      *
-     *     o keyFormat is an integer from the KeyFormat enum, defining the format of the plaintext
+     *     - keyFormat is an integer from the KeyFormat enum, defining the format of the plaintext
      *       key material.
-     *     o keyParams is the characteristics of the key to be imported (as with generateKey or
+     *     - 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 attestKey() for documentation of the
-     *       AuthorizationList schema.
-     *     o encryptedTransportKey is a 256-bit AES key, XORed with a masking key and then encrypted
+     *       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.
-     *     o keyDescription is a KeyDescription, above.
-     *     o encryptedKey is the key material of the key to be imported, in format keyFormat, and
+     *     - 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.
-     *     o tag is the tag produced by the AES-GCM encryption of encryptedKey.
+     *     - tag is the tag produced by the AES-GCM encryption of encryptedKey.
      *
      * So, importWrappedKey does the following:
      *
@@ -415,7 +440,7 @@
      *
      * @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_IDwith a value that has the HardwareAuthenticatorType::PASSWORD bit
+     *        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.
@@ -429,12 +454,9 @@
      *
      * @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);
+    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
@@ -461,15 +483,15 @@
      * 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 inKeyBlobToUpgrade The opaque descriptor returned by generateKey() or importKey();
+     * @param keyBlobToUpgrade The opaque descriptor returned by generateKey() or importKey().
      *
-     * @param inUpgradeParams A parameter list containing any parameters needed to complete the
+     * @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[] inKeyBlobToUpgrade, in KeyParameter[] inUpgradeParams);
+    byte[] upgradeKey(in byte[] keyBlobToUpgrade, in KeyParameter[] upgradeParams);
 
     /**
      * Deletes the key, or key pair, associated with the key blob.  Calling this function on
@@ -477,17 +499,17 @@
      * render the key permanently unusable.  Keys without Tag::ROLLBACK_RESISTANCE may or
      * may not be rendered unusable.
      *
-     * @param inKeyBlob The opaque descriptor returned by generateKey() or importKey();
+     * @param keyBlob The opaque descriptor returned by generateKey() or importKey();
+     *
+     * @return error See the ErrorCode enum.
      */
-    void deleteKey(in byte[] inKeyBlob);
+    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.
-     *
-     * @return error See the ErrorCode enum.
      */
     void deleteAllKeys();
 
@@ -506,28 +528,27 @@
 
     /**
      * Begins a cryptographic operation using the specified key.  If all is well, begin() must
-     * return ErrorCode::OK and create an operation handle which must be passed to subsequent calls
-     * to update(), finish() or abort().
+     * return ErrorCode::OK and create an IKeyMintOperation handle which will be used to perform
+     * the cryptographic operation.
      *
-     * It is critical that each call to begin() be paired with a subsequent call to finish() or
-     * abort(), 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(), update()
-     * or finish() implicitly aborts the operation, in which case abort() need not be called (and
-     * must return ErrorCode::INVALID_OPERATION_HANDLE if called).  IKeyMintDevice implementations
-     * must support 32 concurrent operations.
+     * 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
-     * inParams argument to this method.  If not, begin() must return ErrorCode::INVALID_KEY_BLOB.
+     * 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.  Public key operations, meaning KeyPurpose::ENCRYPT and
-     * KeyPurpose::VERIFY must be allowed to succeed even if authorization requirements are not met.
+     * KeyCharacteristics.
      *
      * -- All Key Types --
      *
@@ -556,9 +577,9 @@
      *
      * 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 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:
+     *   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.
      *
@@ -585,32 +606,30 @@
      *
      * -- RSA Keys --
      *
-     * All RSA key operations must specify exactly one padding mode in inParams.  If unspecified or
+     * 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 and verification 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 inParams.  If unspecified or specified more than once, begin() must return
+     * 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, as appropriate.  Public key operations (KeyPurpose::ENCRYPT
-     * and KeyPurpose::VERIFY) are permitted with unauthorized digest or padding modes.
+     * 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 and verification, 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.
+     * 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 or
-     *   verifying, Digest::NONE is specified for the digest.  No digest is necessary for unpadded
-     *   encryption or decryption.
+     * 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
@@ -622,37 +641,37 @@
      *
      * o PaddingMode::RSA_PKCS1_1_1_5_ENCRYPT padding does not require a digest.
      *
-     * o PaddingMode::RSA_PSS padding requires a digest, which may not be Digest::NONE.  If
-     *   Digest::NONE is specified, the begin() must return ErrorCode::INCOMPATIBLE_DIGEST.  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_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 may not be Digest::NONE.  If
-     *   Digest::NONE is specified, begin() must return ErrorCode::INCOMPATIBLE_DIGEST.  The OAEP
-     *   mask generation function must be MGF1 and the MGF1 digest must be SHA1, regardless of the
-     *   OAEP digest specified.
+     * 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 --
      *
-     * EC key operations must specify exactly one padding mode in inParams.  If unspecified or
-     * specified more than once, begin() must return ErrorCode::UNSUPPORTED_PADDING_MODE.
-     *
      * 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.  Public key operations (KeyPurpose::VERIFY) are
-     * permitted with unauthorized digest or padding.
+     * 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 inParams.  If either value is unspecified or specified more than once,
+     * (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, inParams must specify Tag::MAC_LENGTH, and the specified
+     * 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
@@ -665,48 +684,145 @@
      *
      * 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
+     * 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 inParams.  If a nonce is provided when Tag::CALLER_NONCE is not authorized,
+     * 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 msut generate a random IV/nonce.
+     * 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 inParams.  The specified value must be a
+     * 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 inPurpose The purpose of the operation, one of KeyPurpose::ENCRYPT,
-     *        KeyPurpose::DECRYPT, KeyPurpose::SIGN or KeyPurpose::VERIFY.  Note that for AEAD
+     * @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 inKeyBlob The opaque key descriptor returned by generateKey() or importKey().  The key
+     * @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 inParams Additional parameters for the operation.  If Tag::APPLICATION_ID or
+     * @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, inParams may
+     *        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 inAuthToken Authentication token.  Callers that provide no token must set all numeric
-     *        fields to zero and the MAC must be an empty vector.  TODO: make this field nullable.
-     *        b/173483024.
+     * @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 update(), finish() or abort() operations.
+     *         which is used to perform updateAad(), update(), finish() or abort() operations.
      */
-    BeginResult begin(in KeyPurpose inPurpose,
-               in byte[] inKeyBlob,
-               in KeyParameter[] inParams,
-               in HardwareAuthToken inAuthToken);
+    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 parameters associated with the provided key. This should match the
+     * KeyCharacteristics present in the KeyCreationResult returned by generateKey(),
+     * importKey(), or importWrappedKey().
+     *
+     * @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
index 8c49602..aa7b492 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -16,33 +16,87 @@
 
 package android.hardware.security.keymint;
 
-import android.hardware.security.keymint.ByteArray;
 import android.hardware.security.keymint.HardwareAuthToken;
 import android.hardware.security.keymint.KeyParameter;
-import android.hardware.security.keymint.KeyParameterArray;
 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.
      *
-     * To provide more flexibility for buffer handling, implementations of this method have the
-     * option of consuming less data than was provided.  The caller is responsible for looping to
-     * feed the rest of the data in subsequent calls.  The amount of input consumed must be returned
-     * in the inputConsumed parameter.  Implementations must always consume at least one byte,
-     * unless the operation cannot accept any more; if more than zero bytes are provided and zero
-     * bytes are consumed, callers must consider this an error and abort the operation.
-     * TODO(seleneh) update the code to always consume alll the input data. b/168665179.
-     *
-     * Implementations may also choose how much data to return, as a result of the update.  This is
-     * only relevant for encryption and decryption operations, because signing and verification
-     * return no data until finish.  It is recommended to return data as early as possible, rather
-     * than buffer it.
+     * 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,
@@ -50,8 +104,8 @@
      *
      * == Authorization Enforcement ==
      *
-     * Key authorization enforcement is performed primarily in begin().  The one exception is the
-     * case where the key has:
+     * 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
      *
@@ -68,7 +122,8 @@
      *
      *   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 operationHandle
+     *   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.
@@ -77,75 +132,48 @@
      *
      * -- RSA keys --
      *
-     * For signing and verification operations with Digest::NONE, this method must accept the entire
-     * block to be signed or verified 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.
+     * 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 and verification operations with Digest::NONE, this method must accept the entire
-     * block to be signed or verified 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.)
+     * 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 --
      *
-     * AES GCM mode supports "associated authentication data," provided via the Tag::ASSOCIATED_DATA
-     * tag in the inParams argument.  The associated data may be provided in repeated calls
-     * (important if the data is too large to send in a single block) but must always precede data
-     * to be encrypted or decrypted.  An update call may receive both associated data and data to
-     * encrypt/decrypt, but subsequent updates must not include associated data.  If the caller
-     * provides associated data to an update call after a call that includes data to
-     * encrypt/decrypt, update() must return ErrorCode::INVALID_TAG.
-     *
      * 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().
      *
-     * TODO: update() needs to be refactored b/168665179.
-     *
-     * @param inParams Additional parameters for the operation.  For AEAD modes, this is used to
-     *        specify Tag::ADDITIONAL_DATA.  Note that additional data may be provided in multiple
-     *        calls to update(), but only until input data has been provided.
-     *
      * @param input Data to be processed.  Note that update() may or may not consume all of the data
      *        provided.  See return value.
      *
-     * @param inTimeStampToken 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.
+     * @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 int Amount of data that was consumed by update().  If this is less than the
-     *         amount provided, the caller may provide the remainder in a subsequent call to
-     *         update() or finish().  Every call to update must consume at least one byte, unless
-     *         the input is empty, and implementations should consume as much data as reasonably
-     *         possible for each call.
-     *
-     * @return outParams returns the updated key parameters from the blob, if needed.
-     * operation.
-     *
-     * @return out variable output The output data, if any.
+     * @return byte[] The output data, if any.
      */
-    int update(in @nullable KeyParameterArray inParams,
-               in @nullable byte[] input,
-               in @nullable HardwareAuthToken inAuthToken,
-               in @nullable TimeStampToken inTimeStampToken,
-               out @nullable KeyParameterArray outParams,
-               out @nullable ByteArray output);
+    byte[] update(in byte[] input, in @nullable HardwareAuthToken authToken,
+            in @nullable TimeStampToken timeStampToken);
 
     /**
-     * Finalizes a cryptographic operation begun with begin() and invalidates operation.
+     * 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.
      *
@@ -153,8 +181,7 @@
      * 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.  Verification operations accept the
-     * signature in the signature parameter, and return no output.
+     * Signing operations return the signature as the output.
      *
      * == Authorization enforcement ==
      *
@@ -193,45 +220,35 @@
      * 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 verification and decryption
-     *   operations, the data must be exactly as long as the key.  Otherwise, return
-     *   ErrorCode::INVALID_INPUT_LENGTH.
+     *   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 inputParams
+     *   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.
      *
-     * o PaddingMode::RSA_OAEP.  The digest specified with Tag::DIGEST in inputParams on begin is
-     *   used as the OAEP digest algorithm, MGF1 must be used as the mask generation function and
-     *   and SHA1 must be used as the MGF1 digest algorithm.
-     *
      * -- ECDSA keys --
      *
-     * If the data provided for unpadded signing or verification is too long, truncate it.
+     * 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.
+     *   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.
      *
-     * TODO: update() will need to be refactored into 2 function. b/168665179.
-     *
-     * @param inParams Additional parameters for the operation.  For AEAD modes, this is used to
-     *        specify Tag::ADDITIONAL_DATA, but only if no input data was provided to update().
-     *
      * @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.
      *
@@ -240,28 +257,25 @@
      *
      * @param authToken Authentication token. Can be nullable if not provided.
      *
-     * @param inTimeStampToken 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.
+     * @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 outParams Any output parameters generated by finish().
+     * @param confirmationToken is the confirmation token required by keys with
+     *        Tag::TRUSTED_CONFIRMATION_REQUIRED.
      *
      * @return The output data, if any.
      */
-    byte[] finish(in @nullable KeyParameterArray inParams, in @nullable byte[] input,
-                in @nullable byte[] inSignature,
-                in @nullable HardwareAuthToken authToken,
-                in @nullable TimeStampToken inTimeStampToken,
-                out @nullable KeyParameterArray outParams);
+    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 begin(), freeing all internal resources. If an
-     * operation was finalized, calling update, finish, or abort yields
-     * ErrorCode::INVALID_OPERATION_HANDLE. An operation is finalized if finish or abort was
-     * called on it, or if update returned an ErrorCode.
-     *
-     * @param operationHandle The operation handle returned by begin().  This handle must be
-     *        invalid when abort() returns.
+     * 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.
      */
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..04d91d0
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 self-signed certificate
+ * for 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.
+ *
+ * The first BCC certificate is special because its contained public key, DK_pub, will never change,
+ * making it a permanent, device-unique identifier. Although the remaining keys in the BCC are also
+ * device-unique, they are not necessarily permanent, since they can change when the device software
+ * is updated.
+ *
+ * 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 appropriate 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.
+     */
+    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 X25519 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.
+     *
+     *            EekChain = [ + SignedSignatureKey, SignedEek ]
+     *
+     *            SignedSignatureKey = [              // COSE_Sign1
+     *                protected: bstr .cbor {
+     *                    1 : -8,                     // Algorithm : EdDSA
+     *                },
+     *                unprotected: { },
+     *                payload: bstr .cbor SignatureKey,
+     *                signature: bstr PureEd25519(.cbor SignatureKeySignatureInput)
+     *            ]
+     *
+     *            SignatureKey = {                    // COSE_Key
+     *                 1 : 1,                         // Key type : Octet Key Pair
+     *                 3 : -8,                        // Algorithm : EdDSA
+     *                 -1 : 6,                        // Curve : Ed25519
+     *                 -2 : bstr                      // Ed25519 public key
+     *            }
+     *
+     *            SignatureKeySignatureInput = [
+     *                context: "Signature1",
+     *                body_protected: bstr .cbor {
+     *                    1 : -8,                     // Algorithm : EdDSA
+     *                },
+     *                external_aad: bstr .size 0,
+     *                payload: bstr .cbor SignatureKey
+     *            ]
+     *
+     *            SignedEek = [                       // COSE_Sign1
+     *                protected: bstr .cbor {
+     *                    1 : -8,                     // Algorithm : EdDSA
+     *                },
+     *                unprotected: { },
+     *                payload: bstr .cbor Eek,
+     *                signature: bstr PureEd25519(.cbor EekSignatureInput)
+     *            ]
+     *
+     *            Eek = {                             // 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                       // X25519 public key
+     *            }
+     *
+     *            EekSignatureInput = [
+     *                context: "Signature1",
+     *                body_protected: bstr .cbor {
+     *                    1 : -8,                     // Algorithm : EdDSA
+     *                },
+     *                external_aad: bstr .size 0,
+     *                payload: bstr .cbor Eek
+     *            ]
+     *
+     *        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 certificate 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 keysToSignMac contains the MAC 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 ]
+     *            ]
+     *
+     * @param out ProtectedData contains the encrypted BCC and the ephemeral MAC key used to
+     *        authenticate the keysToSign (see keysToSignMac output argument).
+     */
+    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
index edd4d8f..25fdee3 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyCharacteristics.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyCharacteristics.aidl
@@ -28,9 +28,10 @@
  * 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 {
-    SecurityLevel securityLevel;
+    SecurityLevel securityLevel = SecurityLevel.SOFTWARE;
     KeyParameter[] authorizations;
 }
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
index b149ac9..f93dbba 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
@@ -22,6 +22,7 @@
 /**
  * This structure is returned when a new key is created with generateKey(), importKey() or
  * importWrappedKey().
+ * @hide
  */
 @VintfStability
 parcelable KeyCreationResult {
@@ -36,7 +37,6 @@
      * deciding whether a given tag from `keyParams` argument to the generation/import method should
      * be returned in `keyCharacteristics` are:
      *
-     * - If the IKeyMintDevice cannot fully enforce the semantics of the tag, it should be omitted.
      * - 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.
@@ -45,18 +45,156 @@
      *   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.  If the key parameters provided to the generate/import method
-     * contains Tag::ATTESTATION_CHALLENGE the first certificate will contain an attestation
-     * extension, and will be signed by a factory-installed attestation key and followed by a chain
-     * of certificates leading to an authoritative root.  If there is no attestation challenge, only
-     * one certificate will be returned, and it will be self-signed or contain a fake signature,
-     * depending on whether the key has KeyPurpose::SIGN.  If the generated key is symmetric,
-     * certificateChain will be empty.
+     * 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,
+     *     blockMode                  [4] EXPLICIT SET OF INTEGER OPTIONAL,
+     *     digest                     [5] EXPLICIT SET OF INTEGER OPTIONAL,
+     *     padding                    [6] EXPLICIT SET OF INTEGER OPTIONAL,
+     *     callerNonce                [7] EXPLICIT NULL OPTIONAL,
+     *     minMacLength               [8] EXPLICIT INTEGER OPTIONAL,
+     *     ecCurve                    [10] EXPLICIT INTEGER OPTIONAL,
+     *     rsaPublicExponent          [200] EXPLICIT INTEGER OPTIONAL,
+     *     rollbackResistance         [303] EXPLICIT NULL OPTIONAL,
+     *     activeDateTime             [400] EXPLICIT INTEGER OPTIONAL,
+     *     originationExpireDateTime  [401] EXPLICIT INTEGER OPTIONAL,
+     *     usageExpireDateTime        [402] EXPLICIT INTEGER OPTIONAL,
+     *     userSecureId               [502] 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,
+     * }
      */
     Certificate[] certificateChain;
 }
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl
index 6ad8e3d..da3d521 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl
@@ -18,6 +18,7 @@
 
 /**
  * Formats for key import and export.
+ * @hide
  */
 @VintfStability
 @Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
index d3d7368..8da7578 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
@@ -20,8 +20,10 @@
 
 /**
  * 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
@@ -33,7 +35,7 @@
 
     /* securityLevel is the security level of the IKeyMintDevice implementation accessed
      * through this aidl package.  */
-    SecurityLevel securityLevel;
+    SecurityLevel securityLevel = SecurityLevel.SOFTWARE;
 
     /* keyMintName is the name of the IKeyMintDevice implementation.  */
     @utf8InCpp String keyMintName;
@@ -44,4 +46,11 @@
      *         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
index 0cd53c2..5840c6b 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
@@ -20,7 +20,8 @@
  * 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 outide the secure hardware.
+ * must be guaranteed never to have existed outside the secure hardware.
+ * @hide
  */
 @VintfStability
 @Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl
index f3ed96b..b69e678 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl
@@ -16,16 +16,17 @@
 
 package android.hardware.security.keymint;
 
-import android.hardware.security.keymint.Tag;
 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 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
index a4f5154..59016f2 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyParameterValue.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyParameterValue.aidl
@@ -26,10 +26,10 @@
 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;
 
@@ -45,7 +45,7 @@
     SecurityLevel securityLevel;
 
     /* Other types */
-    boolean boolValue;  // Always true, if present.
+    boolean boolValue; // Always true, if present.
     int integer;
     long longInteger;
     long dateTime;
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
index cb4682e..e141e55 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
@@ -16,17 +16,17 @@
 
 package android.hardware.security.keymint;
 
-
 /**
  * Possible purposes of a key (or pair).
+ * @hide
  */
 @VintfStability
-@Backing(type = "int")
+@Backing(type="int")
 enum KeyPurpose {
-    /* Usable with RSA, EC and AES keys. */
+    /* Usable with RSA, 3DES and AES keys. */
     ENCRYPT = 0,
 
-    /* Usable with RSA, EC and AES keys. */
+    /* Usable with RSA, 3DES and AES keys. */
     DECRYPT = 1,
 
     /* Usable with RSA, EC and HMAC keys. */
@@ -36,8 +36,14 @@
     VERIFY = 3,
 
     /* 4 is reserved */
+
     /* Usable with wrapping keys. */
     WRAP_KEY = 5,
 
-    /* TODO(seleneh) add AGREE_KEY and ATTEST_KEY and their corresponding codes and tests later*/
+    /* 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
index 80b73bd..fbb373b 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/PaddingMode.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/PaddingMode.aidl
@@ -23,6 +23,7 @@
  * 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")
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..5199062
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
@@ -0,0 +1,170 @@
+/*
+ * 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 : {          // COSE_Key
+     *                         1 : 1,      // Key type : Octet Key Pair
+     *                         -1 : 4,     // Curve : X25519
+     *                         -2 : bstr   // Sender X25519 public key
+     *                     }
+     *                     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 : [
+     *             128,                    // Output key length
+     *             protected : bstr .size 0
+     *         ]
+     *     ]
+     *
+     *     ProtectedDataPayload [
+     *         SignedMac,
+     *         Bcc,
+     *     ]
+     *
+     *     SignedMac = [                       // COSE_Sign1
+     *         bstr .cbor {                    // Protected params
+     *             1 : -8,                     // Algorithm : EdDSA
+     *         },
+     *         { },                            // Unprotected params
+     *         bstr .size 32,                  // MAC key
+     *         bstr PureEd25519(DK_priv, .cbor SignedMac_structure)
+     *     ]
+     *
+     *     SignedMac_structure = [
+     *         "Signature1",
+     *         bstr .cbor {                    // Protected params
+     *             1 : -8,                     // Algorithm : EdDSA
+     *         },
+     *         bstr .cbor SignedMacAad
+     *         bstr .size 32                   // MAC key
+     *     ]
+     *
+     *     SignedMacAad = [
+     *         challenge : bstr,
+     *         DeviceInfo
+     *     ]
+     *
+     *     Bcc = [
+     *         PubKey,                        // 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 PubKey   // Subject Public Key
+     *         -4670553 : bstr                // Key Usage
+     *     }
+     *
+     *     BccEntry = [                       // COSE_Sign1
+     *         protected: bstr .cbor {
+     *             1 : -8,                    // Algorithm : EdDSA
+     *         },
+     *         unprotected: { },
+     *         payload: bstr .cbor BccPayload,
+     *         // First entry in the chain is signed by DK_pub, the others are each signed by their
+     *         // immediate predecessor.  See RFC 8032 for signature representation.
+     *         signature: bstr .cbor PureEd25519(SigningKey, bstr .cbor BccEntryInput)
+     *     ]
+     *
+     *     PubKey = {                         // COSE_Key
+     *         1 : 1,                         // Key type : octet key pair
+     *         3 : -8,                        // Algorithm : EdDSA
+     *         4 : 2,                         // Ops: Verify
+     *         -1 : 6,                        // Curve : Ed25519
+     *         -2 : bstr                      // X coordinate, little-endian
+     *     }
+     *
+     *     BccEntryInput = [
+     *         context: "Signature1",
+     *         protected: bstr .cbor {
+     *             1 : -8,                    // Algorithm : EdDSA
+     *         },
+     *         external_aad: bstr .size 0,
+     *         payload: bstr .cbor BccPayload
+     *     ]
+     *
+     *     DeviceInfo = {
+     *         ? "brand" : tstr,
+     *         ? "manufacturer" : tstr,
+     *         ? "product" : tstr,
+     *         ? "model" : tstr,
+     *         ? "board" : tstr,
+     *         ? "vb_state" : "green" / "yellow" / "orange",
+     *         ? "bootloader_state" : "locked" / "unlocked",
+     *         ? "os_version" : tstr,
+     *         ? "system_patch_level" : uint,        // YYYYMMDD
+     *         ? "boot_patch_level" : uint,          // YYYYMMDD
+     *         ? "vendor_patch_level" : uint,        // YYYYMMDD
+     *     }
+     */
+    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
index 10363e9..80c63b2 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/SecurityLevel.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/SecurityLevel.aidl
@@ -17,16 +17,62 @@
 package android.hardware.security.keymint;
 
 /**
- * Device security levels.
+ * 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 {
-    SOFTWARE = 0,
-    TRUSTED_ENVIRONMENT = 1,
     /**
-     * STRONGBOX specifies that the secure hardware satisfies the requirements specified in CDD
-     * 9.11.2.
+     * 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
index f92bf00..1e101ab 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -19,15 +19,16 @@
 import android.hardware.security.keymint.TagType;
 
 // TODO(seleneh) : note aidl currently does not support double nested enum definitions such as
-// ROOT_OF_TRUST = TagType:BYTES | 704.  So we are forced to write definations as
+// ROOT_OF_TRUST = TagType:BYTES | 704.  So we are forced to write definitions as
 // ROOT_OF_TRUST = (9 << 28) for now.  Will need to flip this back later when aidl support is added.
 
 /**
  * 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")
+@Backing(type="int")
 enum Tag {
     /**
      * Tag::INVALID should never be set.  It means you hit an error.
@@ -58,7 +59,7 @@
     ALGORITHM = (1 << 28) /* TagType:ENUM */ | 2,
 
     /**
-     * Tag::KEY_SIZE pecifies the size, in bits, of the key, measuring in the normal way for the
+     * 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
@@ -74,24 +75,23 @@
      * 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 additionalParams 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 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 = (2 << 28) /* 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 additionalParams 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.
+     * 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.
      */
@@ -145,7 +145,7 @@
      * 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_KEY_SIZE.
+     * generateKey() or importKey() must return ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH.
      *
      * Must be hardware-enforced.
      */
@@ -154,9 +154,8 @@
     // Tag 9 reserved
 
     /**
-     * Tag::EC_CURVE specifies the elliptic curve.  EC key generation requests may have
-     * Tag:EC_CURVE, Tag::KEY_SIZE, or both.  If both are provided and the size and curve do not
-     * match, IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
+     * Tag::EC_CURVE specifies the elliptic curve.  Possible values are defined in the EcCurve
+     * enumeration.
      *
      * Must be hardware-enforced.
      */
@@ -187,38 +186,20 @@
      */
     INCLUDE_UNIQUE_ID = (7 << 28) /* TagType:BOOL */ | 202,
 
-     /**
-      * Tag::RSA_OAEP_MGF_DIGEST specifies the MGF1 digest algorithms that may be used with
-      * RSA encryption/decryption with OAEP padding. If the key characteristics supports OAEP
-      * and this tag is absent then SHA1 digest is selected by default for MGF1.
-      *
-      * This tag is repeatable for key generation/import.  If this tag is present in the key
-      * characteristics with one or more values from @4.0::Digest, then for RSA cipher
-      * operations with OAEP Padding, the caller must specify a digest in the additionalParams
-      * argument of begin operation. If this tag is missing or the specified digest is not in
-      * the digests associated with the key then begin operation must fail with
-      * ErrorCode::INCOMPATIBLE_MGF_DIGEST.
-      *
-      * Must be hardware-enforced.
-      */
-     RSA_OAEP_MGF_DIGEST = (2 << 28) /* TagType:ENUM_REP */ | 203,
-
     /**
-     * TODO(seleneh) this tag needs to be deleted from all codes.
+     * 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.
      *
-     * Tag::BLOB_USAGE_REQUIREMENTS specifies the necessary system environment conditions for the
-     * generated key to be used.  Possible values are defined by the KeyBlobUsageRequirements enum.
-     *
-     * This tag is specified by the caller during key generation or import to require that the key
-     * is usable in the specified condition.  If the caller specifies Tag::BLOB_USAGE_REQUIREMENTS
-     * with value KeyBlobUsageRequirements::STANDALONE the IKeyMintDevice must return a key blob
-     * that can be used without file system support.  This is critical for devices with encrypted
-     * disks, where the file system may not be available until after a KeyMint key is used to
-     * decrypt the disk.
+     * 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.
      */
-    BLOB_USAGE_REQUIREMENTS = (1 << 28) /* TagType:ENUM */ | 301,
+    RSA_OAEP_MGF_DIGEST = (2 << 28) /* TagType:ENUM_REP */ | 203,
+
+    // Tag 301 reserved
 
     /**
      * Tag::BOOTLOADER_ONLY specifies only the bootloader can use the key.
@@ -241,7 +222,7 @@
      * ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE.  IKeyMintDevice implementations are not
      * required to support rollback resistance.
      *
-     * Must be hardwared-enforced.
+     * Must be hardware-enforced.
      */
     ROLLBACK_RESISTANCE = (7 << 28) /* TagType:BOOL */ | 303,
 
@@ -249,8 +230,11 @@
     HARDWARE_TYPE = (1 << 28) /* TagType:ENUM */ | 304,
 
     /**
-     * Keys tagged with EARLY_BOOT_ONLY may only be used, or created, during early boot, until
-     * IKeyMintDevice::earlyBootEnded() is called.
+     * 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::INVALID_ARGUMENT.
      */
     EARLY_BOOT_ONLY = (7 << 28) /* TagType:BOOL */ | 305,
 
@@ -304,7 +288,7 @@
      * 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 acommodate at least 8 in-use keys and
+     * 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.
      *
@@ -324,7 +308,7 @@
      * 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 acommodate at least 8 keys.  If an
+     * 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.
      *
@@ -333,6 +317,35 @@
     MAX_USES_PER_BOOT = (3 << 28) /* 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 = (3 << 28) | 405, /* TagType:UINT */
+
+    /**
      * Tag::USER_ID specifies the ID of the Android user that is permitted to use the key.
      *
      * Must not be hardware-enforced.
@@ -354,14 +367,14 @@
      * 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 returrn
+     * 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 returrn
+     * 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
@@ -402,7 +415,7 @@
     /**
      * 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 requies authentication for
+     * 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
@@ -468,10 +481,11 @@
      */
     TRUSTED_USER_PRESENCE_REQUIRED = (7 << 28) /* TagType:BOOL */ | 507,
 
-    /** Tag::TRUSTED_CONFIRMATION_REQUIRED is only applicable to keys with KeyPurpose SIGN, and
+    /**
+     * 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
-     *  CONFIRMATION_TOKEN, as well as the ConfirmatinUI HAL.
+     *  CONFIRMATION_TOKEN, as well as the ConfirmationUI HAL.
      *
      * If an attempt to use a key with this tag does not have a cryptographically valid
      * CONFIRMATION_TOKEN provided to finish() or if the data provided to update()/finish() does not
@@ -491,10 +505,10 @@
 
     /**
      * 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.
+     * that is necessary during all uses of the key.  In particular, calls to exportKey() 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
@@ -517,7 +531,7 @@
      * 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 msut be bound to the key cryptographically, meaning it must not be
+     * 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.
@@ -528,10 +542,9 @@
 
     /**
      * 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.
+     * January 1, 1970.  This tag is optional and informational only, and not enforced by anything.
      *
-     * Tag::CREATED is informational only, and not enforced by anything.  Must be in the
-     * software-enforced list, if provided.
+     * Must be in the software-enforced list, if provided.
      */
     CREATION_DATETIME = (6 << 28) /* TagType:DATE */ | 701,
 
@@ -560,8 +573,8 @@
      * 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.
+     * currently-running OS version must cause begin() 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
@@ -583,9 +596,8 @@
      * 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.
+     * different from the currently-running system patchlevel must cause begin() 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
@@ -617,10 +629,11 @@
      *      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 attestKey().
+     *    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 attestKey or 0 if the tag was not
-     *      provided.
+     *    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
@@ -635,9 +648,9 @@
     UNIQUE_ID = (9 << 28) /* TagType:BYTES */ | 707,
 
     /**
-     * Tag::ATTESTATION_CHALLENGE is used to deliver a "challenge" value to the attestKey() method,
-     * which must place the value in the KeyDescription SEQUENCE of the attestation extension.  See
-     * attestKey().
+     * 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.
      */
@@ -645,7 +658,7 @@
 
     /**
      * Tag::ATTESTATION_APPLICATION_ID identifies the set of applications which may use a key, used
-     * only with attestKey().
+     * 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:
@@ -671,8 +684,8 @@
 
     /**
      * Tag::ATTESTATION_ID_BRAND provides the device's brand name, as returned by Build.BRAND in
-     * Android, to attestKey().  This field must be set only when requesting attestation of the
-     * device's identifiers.
+     * 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
@@ -684,8 +697,8 @@
 
     /**
      * Tag::ATTESTATION_ID_DEVICE provides the device's device name, as returned by Build.DEVICE in
-     * Android, to attestKey().  This field must be set only when requesting attestation of the
-     * device's identifiers.
+     * 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
@@ -697,8 +710,8 @@
 
     /**
      * Tag::ATTESTATION_ID_PRODUCT provides the device's product name, as returned by Build.PRODUCT
-     * in Android, to attestKey().  This field must be set only when requesting attestation of the
-     * device's identifiers.
+     * 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
@@ -721,8 +734,9 @@
     ATTESTATION_ID_SERIAL = (9 << 28) /* TagType:BYTES */ | 713,
 
     /**
-     * Tag::ATTESTATION_ID_IMEI provides the IMEIs for all radios on the device to attestKey().
-     * This field must be set only when requesting attestation of the device's identifiers.
+     * 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
@@ -733,8 +747,9 @@
     ATTESTATION_ID_IMEI = (9 << 28) /* TagType:BYTES */ | 714,
 
     /**
-     * Tag::ATTESTATION_ID_MEID provides the MEIDs for all radios on the device to attestKey().
-     * This field must be set only when requesting attestation of the device's identifiers.
+     * 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
@@ -746,8 +761,8 @@
 
     /**
      * Tag::ATTESTATION_ID_MANUFACTURER provides the device's manufacturer name, as returned by
-     * Build.MANUFACTURER in Android, to attstKey().  This field must be set only when requesting
-     * attestation of the device's identifiers.
+     * 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
@@ -759,8 +774,8 @@
 
     /**
      * Tag::ATTESTATION_ID_MODEL provides the device's model name, as returned by Build.MODEL in
-     * Android, to attestKey().  This field must be set only when requesting attestation of the
-     * device's identifiers.
+     * 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
@@ -774,9 +789,8 @@
      * 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.
+     * different from the currently-running system patchlevel must cause begin() 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
@@ -797,8 +811,8 @@
      * 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.
+     * cause begin() 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
@@ -806,20 +820,20 @@
      * 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
-     * envirionment (mechanism is implementation-defined).
+     * environment (mechanism is implementation-defined).
      *
      * Must be hardware-enforced.
      */
     BOOT_PATCHLEVEL = (3 << 28) /* TagType:UINT */ | 719,
 
     /**
-     * DEVICE_UNIQUE_ATTESTATION is an argument to IKeyMintDevice::attestKey().  It indicates that
-     * attestation using a device-unique key is requested, rather than a batch key.  When a
-     * device-unique key is used, only the attestation certificate is returned; no additional
-     * chained certificates are provided.  It's up to the caller to recognize the device-unique
-     * signing key.  Only SecurityLevel::STRONGBOX IKeyMintDevices may support device-unique
-     * attestations.  SecurityLevel::TRUSTED_ENVIRONMENT IKeyMintDevices must return
-     * ErrorCode::INVALID_ARGUMENT if they receive DEVICE_UNIQUE_ATTESTATION.
+     * 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, only the attestation certificate is
+     * returned; no additional chained certificates are provided.  It's up to the caller to
+     * recognize the device-unique signing key.  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.
      *
@@ -854,11 +868,7 @@
     STORAGE_KEY = (7 << 28) /* TagType:BOOL */ | 722,
 
     /**
-     * Tag::ASSOCIATED_DATA Provides "associated data" for AES-GCM encryption or decryption.  This
-     * tag is provided to update and specifies data that is not encrypted/decrypted, but is used in
-     * computing the GCM tag.
-     *
-     * Must never appear KeyCharacteristics.
+     * TODO: Delete when keystore1 is deleted.
      */
     ASSOCIATED_DATA = (9 << 28) /* TagType:BYTES */ | 1000,
 
@@ -904,4 +914,47 @@
      * Must never appear in KeyCharacteristics.
      */
     CONFIRMATION_TOKEN = (9 << 28) /* 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 = (8 << 28) /* 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 = (9 << 28) /* TagType:BYTES */ | 1007,
+
+    /**
+     * Tag::CERTIFICATE_NOT_BEFORE the beginning of the validity of the certificate in UNIX epoch
+     * time in seconds.  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 = (6 << 28) /* TagType:DATE */ | 1008,
+
+    /**
+     * Tag::CERTIFICATE_NOT_AFTER the end of the validity of the certificate in UNIX epoch time in
+     * seconds.  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 = (6 << 28) /* 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 = (3 << 28) /* 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
index a273af3..1ba6ede 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/TagType.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/TagType.aidl
@@ -18,6 +18,7 @@
 
 /**
  * TagType classifies Tags in Tag.aidl into various groups of data.
+ * @hide
  */
 @VintfStability
 @Backing(type="int")
diff --git a/security/keymint/aidl/default/Android.bp b/security/keymint/aidl/default/Android.bp
index 79697c4..230534c 100644
--- a/security/keymint/aidl/default/Android.bp
+++ b/security/keymint/aidl/default/Android.bp
@@ -1,18 +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"],
+}
+
 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"],
+    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-unstable-ndk_platform",
+        "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",
+        "libcppbor_external",
         "libcrypto",
         "libkeymaster_portable",
         "libkeymint",
@@ -23,4 +38,15 @@
     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.xml b/security/keymint/aidl/default/android.hardware.security.keymint-service.xml
index 73d15a8..4aa05ef 100644
--- a/security/keymint/aidl/default/android.hardware.security.keymint-service.xml
+++ b/security/keymint/aidl/default/android.hardware.security.keymint-service.xml
@@ -3,4 +3,8 @@
         <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
index a710535..8092e34 100644
--- a/security/keymint/aidl/default/service.cpp
+++ b/security/keymint/aidl/default/service.cpp
@@ -21,25 +21,41 @@
 #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 =
-            ndk::SharedRefBase::make<AndroidKeyMintDevice>(SecurityLevel::SOFTWARE);
-
-    keymaster::SoftKeymasterLogger logger;
-    const auto instanceName = std::string(AndroidKeyMintDevice::descriptor) + "/default";
-    LOG(INFO) << "instance: " << instanceName;
-    binder_status_t status =
-            AServiceManager_addService(keyMint->asBinder().get(), instanceName.c_str());
-    CHECK(status == STATUS_OK);
-
+            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
index 17a4613..d5c45e2 100644
--- a/security/keymint/aidl/vts/functional/Android.bp
+++ b/security/keymint/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: "VtsAidlKeyMintTargetTest",
     defaults: [
@@ -21,6 +30,8 @@
         "use_libaidlvintf_gtest_helper_static",
     ],
     srcs: [
+        "AttestKeyTest.cpp",
+        "DeviceUniqueAttestationTest.cpp",
         "KeyMintTest.cpp",
     ],
     shared_libs: [
@@ -30,9 +41,11 @@
         "libkeymint_support",
     ],
     static_libs: [
-        "android.hardware.security.keymint-unstable-ndk_platform",
-        "android.hardware.security.secureclock-unstable-ndk_platform",
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "android.hardware.security.secureclock-V1-ndk_platform",
         "libcppbor_external",
+        "libcppcose_rkp",
+        "libkeymint_remote_prov_support",
         "libkeymint_vts_test_utils",
     ],
     test_suites: [
@@ -60,8 +73,43 @@
         "libkeymint_support",
     ],
     static_libs: [
-        "android.hardware.security.keymint-unstable-ndk_platform",
-        "android.hardware.security.secureclock-unstable-ndk_platform",
-        "libcppbor",
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "android.hardware.security.secureclock-V1-ndk_platform",
+        "libcppbor_external",
+        "libcppcose_rkp",
+        "libgmock_ndk",
+        "libkeymint_remote_prov_support",
+    ],
+}
+
+cc_test {
+    name: "VtsHalRemotelyProvisionedComponentTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: [
+        "VtsRemotelyProvisionedComponentTests.cpp",
+    ],
+    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",
+        "libgmock_ndk",
+        "libkeymaster_portable",
+        "libkeymint",
+        "libkeymint_support",
+        "libkeymint_remote_prov_support",
+        "libkeymint_vts_test_utils",
+        "libpuresoftkeymasterdevice",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
     ],
 }
diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
new file mode 100644
index 0000000..afb2193
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -0,0 +1,600 @@
+/*
+ * 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));
+
+        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
+         */
+        attested_key_characteristics.resize(0);
+        attested_key_cert_chain.resize(0);
+        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.
+        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));
+
+    uint64_t serial_int = 66;
+    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 = 987;
+    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(224)
+                                      .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(224)
+                                          .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, 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 siging 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 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 siging key */, &non_attest_key.keyBlob,
+                    &non_attest_key_characteristics, &non_attest_key_cert_chain));
+
+    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));
+}
+
+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..7009c6e
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.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 "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, int key_size) {
+        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);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size)) << "Key size missing";
+
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+        ASSERT_GT(cert_chain_.size(), 0);
+
+        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)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .AttestationChallenge("challenge")
+                                      .AttestationApplicationId("foo")
+                                      .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION),
+                              &key_blob, &key_characteristics);
+
+    ASSERT_TRUE(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::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)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .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);
+
+    AuthorizationSet 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());
+
+    CheckUniqueAttestationResults(key_blob, key_characteristics, hw_enforced, key_size);
+}
+
+/*
+ * 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;
+    int key_size = 256;
+
+    auto result = GenerateKey(AuthorizationSetBuilder()
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .EcdsaSigningKey(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);
+
+    AuthorizationSet hw_enforced = AuthorizationSetBuilder()
+                                           .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+                                           .Authorization(TAG_NO_AUTH_REQUIRED)
+                                           .EcdsaSigningKey(EcCurve::P_256)
+                                           .Digest(Digest::SHA_2_256)
+                                           .Authorization(TAG_EC_CURVE, EcCurve::P_256)
+                                           .Authorization(TAG_ORIGIN, KeyOrigin::GENERATED)
+                                           .Authorization(TAG_OS_VERSION, os_version())
+                                           .Authorization(TAG_OS_PATCHLEVEL, os_patch_level());
+
+    CheckUniqueAttestationResults(key_blob, key_characteristics, hw_enforced, key_size);
+}
+
+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
index 766c02d..f0dfff1 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -22,15 +22,29 @@
 
 #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::MatchesRegex;
 
 ::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set) {
     if (set.size() == 0)
@@ -45,7 +59,7 @@
 namespace test {
 
 namespace {
-
+typedef KeyMintAidlTestBase::KeyData KeyData;
 // Predicate for testing basic characteristics validity in generation or import.
 bool KeyCharacteristicsBasicallyValid(SecurityLevel secLevel,
                                       const vector<KeyCharacteristics>& key_characteristics) {
@@ -55,6 +69,9 @@
     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);
 
@@ -70,8 +87,66 @@
     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::EC_CURVE,
+        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;
+}
+
+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;
+
 ErrorCode KeyMintAidlTestBase::GetReturnErrorCode(const Status& result) {
     if (result.isOk()) return ErrorCode::OK;
 
@@ -95,6 +170,7 @@
 
     os_version_ = getOsVersion();
     os_patch_level_ = getOsPatchlevel();
+    vendor_patch_level_ = getVendorPatchlevel();
 }
 
 void KeyMintAidlTestBase::SetUp() {
@@ -107,48 +183,48 @@
 }
 
 ErrorCode KeyMintAidlTestBase::GenerateKey(const AuthorizationSet& key_desc,
+                                           const optional<AttestationKey>& attest_key,
                                            vector<uint8_t>* key_blob,
-                                           vector<KeyCharacteristics>* key_characteristics) {
+                                           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.";
 
-    // Aidl does not clear these output parameters if the function returns
-    // error.  This is different from hal where output parameter is always
-    // cleared due to hal returning void.  So now we need to do our own clearing
-    // of the output variables prior to calling keyMint aidl libraries.
-    key_blob->clear();
-    key_characteristics->clear();
-    cert_chain_.clear();
-
     KeyCreationResult creationResult;
-    Status result = keymint_->generateKey(key_desc.vector_data(), &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);
+        *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);
+            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);
+            EXPECT_EQ(cert_chain->size(), 0);
         }
     }
 
     return GetReturnErrorCode(result);
 }
 
-ErrorCode KeyMintAidlTestBase::GenerateKey(const AuthorizationSet& key_desc) {
-    return GenerateKey(key_desc, &key_blob_, &key_characteristics_);
+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,
@@ -163,7 +239,7 @@
     KeyCreationResult creationResult;
     result = keymint_->importKey(key_desc.vector_data(), format,
                                  vector<uint8_t>(key_material.begin(), key_material.end()),
-                                 &creationResult);
+                                 {} /* attestationSigningKeyBlob */, &creationResult);
 
     if (result.isOk()) {
         EXPECT_PRED2(KeyCharacteristicsBasicallyValid, SecLevel(),
@@ -272,7 +348,7 @@
     SCOPED_TRACE("Begin");
     Status result;
     BeginResult out;
-    result = keymint_->begin(purpose, key_blob, in_params.vector_data(), HardwareAuthToken(), &out);
+    result = keymint_->begin(purpose, key_blob, in_params.vector_data(), std::nullopt, &out);
 
     if (result.isOk()) {
         *out_params = out.params;
@@ -290,7 +366,7 @@
     Status result;
     BeginResult out;
 
-    result = keymint_->begin(purpose, key_blob, in_params.vector_data(), HardwareAuthToken(), &out);
+    result = keymint_->begin(purpose, key_blob, in_params.vector_data(), std::nullopt, &out);
 
     if (result.isOk()) {
         *out_params = out.params;
@@ -316,122 +392,50 @@
     return result;
 }
 
-ErrorCode KeyMintAidlTestBase::Update(const AuthorizationSet& in_params, const string& input,
-                                      AuthorizationSet* out_params, string* output,
-                                      int32_t* input_consumed) {
+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;
-    EXPECT_NE(op_, nullptr);
-    if (!op_) {
-        return ErrorCode::UNEXPECTED_NULL_POINTER;
-    }
+    if (!output) return ErrorCode::UNEXPECTED_NULL_POINTER;
 
-    KeyParameterArray key_params;
-    key_params.params = in_params.vector_data();
+    std::vector<uint8_t> o_put;
+    result = op_->update(vector<uint8_t>(input.begin(), input.end()), {}, {}, &o_put);
 
-    KeyParameterArray in_keyParams;
-    in_keyParams.params = in_params.vector_data();
-
-    optional<KeyParameterArray> out_keyParams;
-    optional<ByteArray> o_put;
-    result = op_->update(in_keyParams, vector<uint8_t>(input.begin(), input.end()), {}, {},
-                         &out_keyParams, &o_put, input_consumed);
-
-    if (result.isOk()) {
-        if (o_put) {
-            output->append(o_put->data.begin(), o_put->data.end());
-        }
-
-        if (out_keyParams) {
-            out_params->push_back(AuthorizationSet(out_keyParams->params));
-        }
-    }
+    if (result.isOk()) output->append(o_put.begin(), o_put.end());
 
     return GetReturnErrorCode(result);
 }
 
-ErrorCode KeyMintAidlTestBase::Update(const string& input, string* out, int32_t* input_consumed) {
-    SCOPED_TRACE("Update");
-    AuthorizationSet out_params;
-    ErrorCode result =
-            Update(AuthorizationSet() /* in_params */, input, &out_params, out, input_consumed);
-    EXPECT_TRUE(out_params.empty());
-    return result;
-}
-
-ErrorCode KeyMintAidlTestBase::Finish(const AuthorizationSet& in_params, const string& input,
-                                      const string& signature, AuthorizationSet* out_params,
+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;
-    }
-
-    KeyParameterArray key_params;
-    key_params.params = in_params.vector_data();
-
-    KeyParameterArray in_keyParams;
-    in_keyParams.params = in_params.vector_data();
-
-    optional<KeyParameterArray> out_keyParams;
-    optional<vector<uint8_t>> o_put;
+    if (!op_) return ErrorCode::UNEXPECTED_NULL_POINTER;
 
     vector<uint8_t> oPut;
-    result = op_->finish(in_keyParams, vector<uint8_t>(input.begin(), input.end()),
-                         vector<uint8_t>(signature.begin(), signature.end()), {}, {},
-                         &out_keyParams, &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()) {
-        if (out_keyParams) {
-            out_params->push_back(AuthorizationSet(out_keyParams->params));
-        }
+    if (result.isOk()) output->append(oPut.begin(), oPut.end());
 
-        output->append(oPut.begin(), oPut.end());
-    }
-
-    op_.reset();
+    op_ = {};
     return GetReturnErrorCode(result);
 }
 
-ErrorCode KeyMintAidlTestBase::Finish(const string& message, string* output) {
-    SCOPED_TRACE("Finish");
-    AuthorizationSet out_params;
-    string finish_output;
-    ErrorCode result = Finish(AuthorizationSet() /* in_params */, message, "" /* signature */,
-                              &out_params, output);
-    if (result != ErrorCode::OK) {
-        return result;
-    }
-    EXPECT_EQ(0U, out_params.size());
-    return result;
-}
-
-ErrorCode KeyMintAidlTestBase::Finish(const string& message, const string& signature,
-                                      string* output) {
-    SCOPED_TRACE("Finish");
-    AuthorizationSet out_params;
-    ErrorCode result =
-            Finish(AuthorizationSet() /* in_params */, message, signature, &out_params, output);
-
-    if (result != ErrorCode::OK) {
-        return result;
-    }
-
-    EXPECT_EQ(0U, out_params.size());
-    return result;
-}
-
 ErrorCode KeyMintAidlTestBase::Abort(const std::shared_ptr<IKeyMintOperation>& op) {
     SCOPED_TRACE("Abort");
 
     EXPECT_NE(op, nullptr);
-    if (!op) {
-        return ErrorCode::UNEXPECTED_NULL_POINTER;
-    }
+    if (!op) return ErrorCode::UNEXPECTED_NULL_POINTER;
 
     Status retval = op->abort();
     EXPECT_TRUE(retval.isOk());
@@ -442,9 +446,7 @@
     SCOPED_TRACE("Abort");
 
     EXPECT_NE(op_, nullptr);
-    if (!op_) {
-        return ErrorCode::UNEXPECTED_NULL_POINTER;
-    }
+    if (!op_) return ErrorCode::UNEXPECTED_NULL_POINTER;
 
     Status retval = op_->abort();
     return static_cast<ErrorCode>(retval.getServiceSpecificError());
@@ -458,35 +460,30 @@
     }
 }
 
+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, &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;
-    int32_t consumed = 0;
-    AuthorizationSet update_params;
-    AuthorizationSet update_out_params;
-    result = Update(update_params, message, &update_out_params, &output, &consumed);
-    EXPECT_EQ(ErrorCode::OK, result);
-    if (result != ErrorCode::OK) {
-        return "";
-    }
-
-    string unused;
-    AuthorizationSet finish_params;
-    AuthorizationSet finish_out_params;
-    EXPECT_EQ(ErrorCode::OK,
-              Finish(finish_params, message.substr(consumed), unused, &finish_out_params, &output));
-
-    out_params->push_back(begin_out_params);
-    out_params->push_back(finish_out_params);
+    EXPECT_EQ(ErrorCode::OK, Finish(message, &output));
     return output;
 }
 
@@ -576,21 +573,9 @@
     ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::VERIFY, key_blob, params, &begin_out_params));
 
     string output;
-    AuthorizationSet update_params;
-    AuthorizationSet update_out_params;
-    int32_t consumed;
-    ASSERT_EQ(ErrorCode::OK,
-              Update(update_params, message, &update_out_params, &output, &consumed));
+    EXPECT_EQ(ErrorCode::OK, Finish(message, signature, &output));
     EXPECT_TRUE(output.empty());
-    EXPECT_GT(consumed, 0U);
-
-    string unused;
-    AuthorizationSet finish_params;
-    AuthorizationSet finish_out_params;
-    EXPECT_EQ(ErrorCode::OK, Finish(finish_params, message.substr(consumed), signature,
-                                    &finish_out_params, &output));
-    op_.reset();
-    EXPECT_TRUE(output.empty());
+    op_ = {};
 }
 
 void KeyMintAidlTestBase::VerifyMessage(const string& message, const string& signature,
@@ -759,6 +744,15 @@
                 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 {};
         }
@@ -766,6 +760,68 @@
     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};
@@ -824,22 +880,491 @@
     return (found == key_characteristics.end()) ? kEmptyAuthList : found->authorizations;
 }
 
-const vector<KeyParameter>& KeyMintAidlTestBase::HwEnforcedAuthorizations(
-        const vector<KeyCharacteristics>& key_characteristics) {
-    auto found =
-            std::find_if(key_characteristics.begin(), key_characteristics.end(), [](auto& entry) {
-                return entry.securityLevel == SecurityLevel::STRONGBOX ||
-                       entry.securityLevel == SecurityLevel::TRUSTED_ENVIRONMENT;
-            });
+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;
 }
 
-const vector<KeyParameter>& KeyMintAidlTestBase::SwEnforcedAuthorizations(
-        const vector<KeyCharacteristics>& key_characteristics) {
-    auto found = std::find_if(
-            key_characteristics.begin(), key_characteristics.end(),
-            [](auto& entry) { return entry.securityLevel == SecurityLevel::SOFTWARE; });
-    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 {};
+    }
+
+    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_keymaster_version;
+    SecurityLevel att_attestation_security_level;
+    SecurityLevel att_keymaster_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_keymaster_version,           //
+                                          &att_keymaster_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_keymaster_version, 100U);
+    EXPECT_EQ(security_level, att_keymaster_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
+    // keymaster 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));
+    }
+
+    // Alternatively this checks the opposite - a false boolean tag (one that isn't provided in
+    // the authorization list during key generation) isn't being attested to in the certificate.
+    EXPECT_FALSE(expected_sw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
+    EXPECT_FALSE(att_sw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
+    EXPECT_FALSE(expected_hw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
+    EXPECT_FALSE(att_hw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_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();
+    auto a = filtered_tags(expected_sw_enforced);
+    auto b = filtered_tags(att_sw_enforced);
+    EXPECT_EQ(a, b);
+
+    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) {
+    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) {
+            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 testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
+                                                payload->value());
+    ASSERT_TRUE(testTag) << "Tag calculation failed: " << testTag.message();
+
+    if (testMode) {
+        EXPECT_EQ(*testTag, extractedTag);
+    } else {
+        EXPECT_NE(*testTag, 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
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index c1a1dd9..88998d5 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -21,20 +21,28 @@
 #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;
@@ -43,6 +51,14 @@
 
 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()) {
@@ -55,12 +71,21 @@
     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_; }
 
     ErrorCode GetReturnErrorCode(const Status& result);
-    ErrorCode GenerateKey(const AuthorizationSet& key_desc, vector<uint8_t>* key_blob,
-                          vector<KeyCharacteristics>* key_characteristics);
 
-    ErrorCode GenerateKey(const AuthorizationSet& key_desc);
+    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,
@@ -89,15 +114,14 @@
                     AuthorizationSet* out_params);
     ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params);
 
-    ErrorCode Update(const AuthorizationSet& in_params, const string& input,
-                     AuthorizationSet* out_params, string* output, int32_t* input_consumed);
-    ErrorCode Update(const string& input, string* out, int32_t* input_consumed);
+    ErrorCode UpdateAad(const string& input);
+    ErrorCode Update(const string& input, string* output);
 
-    ErrorCode Finish(const AuthorizationSet& in_params, const string& input,
-                     const string& signature, AuthorizationSet* out_params, string* output);
-    ErrorCode Finish(const string& message, string* output);
     ErrorCode Finish(const string& message, const string& signature, string* output);
-    ErrorCode Finish(string* output) { return Finish(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);
@@ -106,7 +130,9 @@
     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);
@@ -149,12 +175,66 @@
 
     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) {
+        /* AES */
+        KeyData aesKeyData;
+        ErrorCode errorCode = GenerateKey(AuthorizationSetBuilder()
+                                                  .AesEncryptionKey(128)
+                                                  .Authorization(tagToTest)
+                                                  .BlockMode(BlockMode::ECB)
+                                                  .Padding(PaddingMode::NONE)
+                                                  .Authorization(TAG_NO_AUTH_REQUIRED),
+                                          &aesKeyData.blob, &aesKeyData.characteristics);
+        EXPECT_EQ(expectedReturn, errorCode);
+
+        /* HMAC */
+        KeyData hmacKeyData;
+        errorCode = GenerateKey(AuthorizationSetBuilder()
+                                        .HmacKey(128)
+                                        .Authorization(tagToTest)
+                                        .Digest(Digest::SHA_2_256)
+                                        .Authorization(TAG_MIN_MAC_LENGTH, 128)
+                                        .Authorization(TAG_NO_AUTH_REQUIRED),
+                                &hmacKeyData.blob, &hmacKeyData.characteristics);
+        EXPECT_EQ(expectedReturn, errorCode);
+
+        /* RSA */
+        KeyData rsaKeyData;
+        errorCode = GenerateKey(AuthorizationSetBuilder()
+                                        .RsaSigningKey(2048, 65537)
+                                        .Authorization(tagToTest)
+                                        .Digest(Digest::NONE)
+                                        .Padding(PaddingMode::NONE)
+                                        .Authorization(TAG_NO_AUTH_REQUIRED)
+                                        .SetDefaultValidity(),
+                                &rsaKeyData.blob, &rsaKeyData.characteristics);
+        EXPECT_EQ(expectedReturn, errorCode);
+
+        /* ECDSA */
+        KeyData ecdsaKeyData;
+        errorCode = GenerateKey(AuthorizationSetBuilder()
+                                        .EcdsaSigningKey(256)
+                                        .Authorization(tagToTest)
+                                        .Digest(Digest::SHA_2_256)
+                                        .Authorization(TAG_NO_AUTH_REQUIRED)
+                                        .SetDefaultValidity(),
+                                &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();
 
@@ -175,15 +255,19 @@
     inline const vector<KeyParameter>& SecLevelAuthorizations() {
         return SecLevelAuthorizations(key_characteristics_);
     }
-    const vector<KeyParameter>& HwEnforcedAuthorizations(
-            const vector<KeyCharacteristics>& key_characteristics);
-    const vector<KeyParameter>& SwEnforcedAuthorizations(
-            const vector<KeyCharacteristics>& key_characteristics);
+    const vector<KeyParameter>& SecLevelAuthorizations(
+            const vector<KeyCharacteristics>& key_characteristics, SecurityLevel securityLevel);
 
-  private:
+    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_;
 
     SecurityLevel securityLevel_;
     string name_;
@@ -191,10 +275,36 @@
     long challenge_;
 };
 
+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);
+
 #define INSTANTIATE_KEYMINT_AIDL_TEST(name)                                          \
     INSTANTIATE_TEST_SUITE_P(PerInstance, name,                                      \
                              testing::ValuesIn(KeyMintAidlTestBase::build_params()), \
-                             ::android::PrintInstanceNameToString)
+                             ::android::PrintInstanceNameToString);                  \
+    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name);
 
 }  // namespace test
 
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index e7c94f3..f9a99aa 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -14,41 +14,33 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "keymint_5_test"
+#define LOG_TAG "keymint_1_test"
 #include <cutils/log.h>
 
 #include <signal.h>
 #include <iostream>
 
+#include <openssl/ec.h>
 #include <openssl/evp.h>
 #include <openssl/mem.h>
-#include <openssl/x509.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/attestation_record.h>
 #include <keymint_support/key_param_output.h>
 #include <keymint_support/openssl_utils.h>
 
 #include "KeyMintAidlTestBase.h"
 
-static bool arm_deleteAllKeys = false;
-static bool dump_Attestations = false;
-
 using aidl::android::hardware::security::keymint::AuthorizationSet;
 using aidl::android::hardware::security::keymint::KeyCharacteristics;
 using aidl::android::hardware::security::keymint::KeyFormat;
 
-namespace aidl::android::hardware::security::keymint {
-
-bool operator==(const keymint::AuthorizationSet& a, const keymint::AuthorizationSet& b) {
-    return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
-}
-
-}  // namespace aidl::android::hardware::security::keymint
-
 namespace std {
 
 using namespace aidl::android::hardware::security::keymint;
@@ -75,8 +67,11 @@
 
 namespace {
 
+bool check_patchLevels = false;
+
 template <TagType tag_type, Tag tag, typename ValueT>
-bool contains(vector<KeyParameter>& set, TypedTag<tag_type, tag> ttag, ValueT expected_value) {
+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;
@@ -87,7 +82,7 @@
 }
 
 template <TagType tag_type, Tag tag>
-bool contains(vector<KeyParameter>& set, TypedTag<tag_type, 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());
@@ -144,6 +139,54 @@
                 "d5f33645e8ed8b4a1cb3cc4a1d67987399f2a09f5b3fb68c88d5e5d90ac3"
                 "3492d6");
 
+/*
+ * 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 ec_256_key =
         hex2str("308187020100301306072a8648ce3d020106082a8648ce3d030107046d30"
                 "6b0201010420737c2ecd7b8d1940bf2930aa9b4ed3ff941eed09366bc032"
@@ -180,281 +223,6 @@
     void operator()(RSA* p) { RSA_free(p); }
 };
 
-char nibble2hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
-                       '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
-
-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;
-}
-
-X509* parse_cert_blob(const vector<uint8_t>& blob) {
-    const uint8_t* p = blob.data();
-    return d2i_X509(nullptr, &p, blob.size());
-}
-
-bool verify_chain(const vector<Certificate>& chain) {
-    for (size_t i = 0; i < chain.size(); ++i) {
-        X509_Ptr key_cert(parse_cert_blob(chain[i].encodedCertificate));
-        X509_Ptr signing_cert;
-        if (i < chain.size() - 1) {
-            signing_cert.reset(parse_cert_blob(chain[i + 1].encodedCertificate));
-        } else {
-            signing_cert.reset(parse_cert_blob(chain[i].encodedCertificate));
-        }
-        EXPECT_TRUE(!!key_cert.get() && !!signing_cert.get());
-        if (!key_cert.get() || !signing_cert.get()) return false;
-
-        EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get()));
-        EXPECT_TRUE(!!signing_pubkey.get());
-        if (!signing_pubkey.get()) return false;
-
-        EXPECT_EQ(1, X509_verify(key_cert.get(), signing_pubkey.get()))
-                << "Verification of certificate " << i << " failed "
-                << "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL);
-
-        char* cert_issuer =  //
-                X509_NAME_oneline(X509_get_issuer_name(key_cert.get()), nullptr, 0);
-        char* signer_subj =
-                X509_NAME_oneline(X509_get_subject_name(signing_cert.get()), nullptr, 0);
-        EXPECT_STREQ(cert_issuer, signer_subj) << "Cert " << i << " has wrong issuer.";
-        if (i == 0) {
-            char* cert_sub = X509_NAME_oneline(X509_get_subject_name(key_cert.get()), nullptr, 0);
-            EXPECT_STREQ("/CN=Android Keystore Key", cert_sub)
-                    << "Cert " << i << " has wrong subject.";
-            OPENSSL_free(cert_sub);
-        }
-
-        OPENSSL_free(cert_issuer);
-        OPENSSL_free(signer_subj);
-
-        if (dump_Attestations) std::cout << bin2hex(chain[i].encodedCertificate) << std::endl;
-    }
-
-    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 tag_in_list(const KeyParameter& entry) {
-    // 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 tag_list = {
-            Tag::BLOB_USAGE_REQUIREMENTS,  //
-            Tag::CREATION_DATETIME,        //
-            Tag::EC_CURVE,
-            Tag::HARDWARE_TYPE,
-            Tag::INCLUDE_UNIQUE_ID,
-    };
-    return std::find(tag_list.begin(), tag_list.end(), entry.tag) != tag_list.end();
-}
-
-AuthorizationSet filtered_tags(const AuthorizationSet& set) {
-    AuthorizationSet filtered;
-    std::remove_copy_if(set.begin(), set.end(), std::back_inserter(filtered), tag_in_list);
-    return filtered;
-}
-
-bool avb_verification_enabled() {
-    char value[PROPERTY_VALUE_MAX];
-    return property_get("ro.boot.vbmeta.device_state", value, "") != 0;
-}
-
-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_keymaster_version;
-    SecurityLevel att_attestation_security_level;
-    SecurityLevel att_keymaster_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_keymaster_version,           //
-                                          &att_keymaster_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_GE(att_attestation_version, 3U);
-
-    expected_sw_enforced.push_back(TAG_ATTESTATION_APPLICATION_ID,
-                                   vector<uint8_t>(app_id.begin(), app_id.end()));
-
-    EXPECT_GE(att_keymaster_version, 4U);
-    EXPECT_EQ(security_level, att_keymaster_security_level);
-    EXPECT_EQ(security_level, att_attestation_security_level);
-
-    EXPECT_EQ(challenge.length(), att_challenge.size());
-    EXPECT_EQ(0, memcmp(challenge.data(), att_challenge.data(), challenge.length()));
-
-    char property_value[PROPERTY_VALUE_MAX] = {};
-    // TODO(b/136282179): When running under VTS-on-GSI the TEE-backed
-    // keymaster 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::dateTime>());
-                // 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));
-    }
-
-    // Alternatively this checks the opposite - a false boolean tag (one that isn't provided in
-    // the authorization list during key generation) isn't being attested to in the certificate.
-    EXPECT_FALSE(expected_sw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
-    EXPECT_FALSE(att_sw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
-    EXPECT_FALSE(expected_hw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
-    EXPECT_FALSE(att_hw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_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;
-}
-
 std::string make_string(const uint8_t* data, size_t length) {
     return std::string(reinterpret_cast<const char*>(data), length);
 }
@@ -494,42 +262,371 @@
     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) {
-        // 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));
+        AuthorizationSet auths = CheckCommonParams(keyCharacteristics);
         EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
         EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::VERIFY));
 
-        // 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::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));
+        EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::VERIFY));
+    }
+
+    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));
 
+        // Check OS details match the original hardware info.
         auto os_ver = auths.GetTagValue(TAG_OS_VERSION);
-        ASSERT_TRUE(os_ver);
+        EXPECT_TRUE(os_ver);
         EXPECT_EQ(*os_ver, os_version());
-
         auto os_pl = auths.GetTagValue(TAG_OS_PATCHLEVEL);
-        ASSERT_TRUE(os_pl);
+        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);
+
+                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);
+                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);
+                }
+
+                auto result = GenerateKey(builder, &key_blob, &key_characteristics);
+                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);
+                    auto result = Begin(KeyPurpose::ENCRYPT, params);
+                    EXPECT_TRUE(result == ErrorCode::INCOMPATIBLE_PADDING_MODE ||
+                                result == ErrorCode::INVALID_KEY_BLOB);
+                } 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.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);
+
+                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);
+
+                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
@@ -542,7 +639,8 @@
         ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                      .RsaSigningKey(key_size, 65537)
                                                      .Digest(Digest::NONE)
-                                                     .Padding(PaddingMode::NONE),
+                                                     .Padding(PaddingMode::NONE)
+                                                     .SetDefaultValidity(),
                                              &key_blob, &key_characteristics));
 
         ASSERT_GT(key_blob.size(), 0U);
@@ -560,25 +658,335 @@
 }
 
 /*
- * NewKeyGenerationTest.Rsa
+ * NewKeyGenerationTest.RsaWithAttestation
  *
  * Verifies that keymint can generate all required RSA key sizes, 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);
+
+        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);
+
+        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);
+
+        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);
+
+    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)
-                                                     .AttestationChallenge(challenge)
-                                                     .AttestationApplicationId(app_id)
-                                                     .Authorization(TAG_NO_AUTH_REQUIRED),
+                                                     .Authorization(TAG_USAGE_COUNT_LIMIT, 1)
+                                                     .SetDefaultValidity(),
                                              &key_blob, &key_characteristics));
 
         ASSERT_GT(key_blob.size(), 0U);
@@ -591,8 +999,73 @@
                 << "Key size " << key_size << "missing";
         EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
 
-        EXPECT_TRUE(verify_chain(cert_chain_));
+        // 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);
+
+        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);
@@ -617,7 +1090,8 @@
                   GenerateKey(AuthorizationSetBuilder()
                                       .RsaSigningKey(key_size, 65537)
                                       .Digest(Digest::NONE)
-                                      .Padding(PaddingMode::NONE),
+                                      .Padding(PaddingMode::NONE)
+                                      .SetDefaultValidity(),
                               &key_blob, &key_characteristics));
     }
 }
@@ -633,7 +1107,8 @@
               GenerateKey(AuthorizationSetBuilder()
                                   .Authorization(TAG_ALGORITHM, Algorithm::RSA)
                                   .Authorization(TAG_RSA_PUBLIC_EXPONENT, 3U)
-                                  .SigningKey()));
+                                  .SigningKey()
+                                  .SetDefaultValidity()));
 }
 
 /*
@@ -646,10 +1121,11 @@
     for (auto key_size : ValidKeySizes(Algorithm::EC)) {
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
-        ASSERT_EQ(ErrorCode::OK,
-                  GenerateKey(
-                          AuthorizationSetBuilder().EcdsaSigningKey(key_size).Digest(Digest::NONE),
-                          &key_blob, &key_characteristics));
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .EcdsaSigningKey(key_size)
+                                                     .Digest(Digest::NONE)
+                                                     .SetDefaultValidity(),
+                                             &key_blob, &key_characteristics));
         ASSERT_GT(key_blob.size(), 0U);
         CheckBaseParams(key_characteristics);
 
@@ -664,6 +1140,246 @@
 }
 
 /*
+ * 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 key_size : ValidKeySizes(Algorithm::EC)) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .EcdsaSigningKey(key_size)
+                                      .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);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                << "Key size " << key_size << "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.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 key_size : ValidKeySizes(Algorithm::EC)) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .EcdsaSigningKey(key_size)
+                                      .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);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                << "Key size " << key_size << "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 key_size : ValidKeySizes(Algorithm::EC)) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .EcdsaSigningKey(key_size)
+                                                     .Digest(Digest::NONE)
+                                                     .AttestationApplicationId(app_id)
+                                                     .SetDefaultValidity(),
+                                             &key_blob, &key_characteristics));
+
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                << "Key size " << key_size << "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";
+    auto key_size = 256;
+    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(key_size)
+                                                     .Digest(Digest::NONE)
+                                                     .AttestationChallenge(challenge)
+                                                     .AttestationApplicationId(app_id)
+                                                     .SetDefaultValidity(),
+                                             &key_blob, &key_characteristics));
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                << "Key size " << key_size << "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 key_size : ValidKeySizes(Algorithm::EC)) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .EcdsaSigningKey(key_size)
+                                                     .Digest(Digest::NONE)
+                                                     .Authorization(TAG_USAGE_COUNT_LIMIT, 1)
+                                                     .SetDefaultValidity(),
+                                             &key_blob, &key_characteristics));
+
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+        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.EcdsaDefaultSize
  *
  * Verifies that failing to specify a key size for EC key generation returns
@@ -674,7 +1390,8 @@
               GenerateKey(AuthorizationSetBuilder()
                                   .Authorization(TAG_ALGORITHM, Algorithm::EC)
                                   .SigningKey()
-                                  .Digest(Digest::NONE)));
+                                  .Digest(Digest::NONE)
+                                  .SetDefaultValidity()));
 }
 
 /*
@@ -687,14 +1404,17 @@
     for (auto key_size : InvalidKeySizes(Algorithm::EC)) {
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
-        ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
-                  GenerateKey(
-                          AuthorizationSetBuilder().EcdsaSigningKey(key_size).Digest(Digest::NONE),
-                          &key_blob, &key_characteristics));
+        ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE, GenerateKey(AuthorizationSetBuilder()
+                                                                       .EcdsaSigningKey(key_size)
+                                                                       .Digest(Digest::NONE)
+                                                                       .SetDefaultValidity(),
+                                                               &key_blob, &key_characteristics));
     }
 
-    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
-              GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(190).Digest(Digest::NONE)));
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE, GenerateKey(AuthorizationSetBuilder()
+                                                                   .EcdsaSigningKey(190)
+                                                                   .Digest(Digest::NONE)
+                                                                   .SetDefaultValidity()));
 }
 
 /*
@@ -710,7 +1430,8 @@
               GenerateKey(AuthorizationSetBuilder()
                                   .EcdsaSigningKey(224)
                                   .Authorization(TAG_EC_CURVE, EcCurve::P_256)
-                                  .Digest(Digest::NONE)));
+                                  .Digest(Digest::NONE)
+                                  .SetDefaultValidity()));
 }
 
 /*
@@ -721,8 +1442,10 @@
 TEST_P(NewKeyGenerationTest, EcdsaAllValidSizes) {
     auto valid_sizes = ValidKeySizes(Algorithm::EC);
     for (size_t size : valid_sizes) {
-        EXPECT_EQ(ErrorCode::OK,
-                  GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(size).Digest(Digest::NONE)))
+        EXPECT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .EcdsaSigningKey(size)
+                                                     .Digest(Digest::NONE)
+                                                     .SetDefaultValidity()))
                 << "Failed to generate size: " << size;
         CheckedDeleteKey();
     }
@@ -741,8 +1464,10 @@
         digest = Digest::SHA_2_512;
     }
     for (auto curve : ValidCurves()) {
-        EXPECT_EQ(ErrorCode::OK,
-                  GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(curve).Digest(digest)))
+        EXPECT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .EcdsaSigningKey(curve)
+                                                     .Digest(digest)
+                                                     .SetDefaultValidity()))
                 << "Failed to generate key on curve: " << curve;
         CheckedDeleteKey();
     }
@@ -778,6 +1503,79 @@
 }
 
 /*
+ * 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);
+
+        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);
+
+        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.
@@ -873,6 +1671,47 @@
                                   .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;
@@ -887,7 +1726,8 @@
                                                  .RsaSigningKey(2048, 65537)
                                                  .Digest(Digest::NONE)
                                                  .Padding(PaddingMode::NONE)
-                                                 .Authorization(TAG_NO_AUTH_REQUIRED)));
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .SetDefaultValidity()));
     string message = "12345678901234567890123456789012";
     string signature = SignMessage(
             message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
@@ -905,7 +1745,8 @@
                                                  .Digest(Digest::NONE)
                                                  .Padding(PaddingMode::NONE)
                                                  .Authorization(TAG_APPLICATION_ID, "clientid")
-                                                 .Authorization(TAG_APPLICATION_DATA, "appdata")));
+                                                 .Authorization(TAG_APPLICATION_DATA, "appdata")
+                                                 .SetDefaultValidity()));
     EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
               Begin(KeyPurpose::SIGN,
                     AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
@@ -941,7 +1782,8 @@
                                                  .RsaSigningKey(2048, 65537)
                                                  .Digest(Digest::SHA_2_256)
                                                  .Padding(PaddingMode::RSA_PSS)
-                                                 .Authorization(TAG_NO_AUTH_REQUIRED)));
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .SetDefaultValidity()));
     // Use large message, which won't work without digesting.
     string message(1024, 'a');
     string signature = SignMessage(
@@ -960,7 +1802,8 @@
                                                  .RsaSigningKey(2048, 65537)
                                                  .Digest(Digest::NONE)
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                 .Padding(PaddingMode::NONE)));
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
     string message = "12345678901234567890123456789012";
     string signature;
 
@@ -979,13 +1822,13 @@
  */
 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)));
+    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,
@@ -1005,7 +1848,8 @@
                                                  .RsaSigningKey(2048, 65537)
                                                  .Digest(Digest::SHA_2_256)
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                                                 .SetDefaultValidity()));
     string message(1024, 'a');
     string signature = SignMessage(message, AuthorizationSetBuilder()
                                                     .Digest(Digest::SHA_2_256)
@@ -1022,7 +1866,8 @@
                                                  .RsaSigningKey(2048, 65537)
                                                  .Digest(Digest::NONE)
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                                                 .SetDefaultValidity()));
     string message(53, 'a');
     string signature = SignMessage(message, AuthorizationSetBuilder()
                                                     .Digest(Digest::NONE)
@@ -1040,7 +1885,8 @@
                                                  .RsaSigningKey(2048, 65537)
                                                  .Digest(Digest::NONE)
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                                                 .SetDefaultValidity()));
     string message(257, 'a');
 
     EXPECT_EQ(ErrorCode::OK,
@@ -1070,7 +1916,8 @@
                                                  .RsaSigningKey(1024, 65537)
                                                  .Digest(Digest::SHA_2_512)
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                 .Padding(PaddingMode::RSA_PSS)));
+                                                 .Padding(PaddingMode::RSA_PSS)
+                                                 .SetDefaultValidity()));
     EXPECT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
               Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
                                               .Digest(Digest::SHA_2_512)
@@ -1088,7 +1935,8 @@
                                                  .RsaSigningKey(2048, 65537)
                                                  .Digest(Digest::NONE)
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                                                 .SetDefaultValidity()));
     // One byte too long
     string message(2048 / 8 + 1, 'a');
     ASSERT_EQ(ErrorCode::OK,
@@ -1122,7 +1970,8 @@
                                                  .RsaSigningKey(2048, 65537)
                                                  .Digest(Digest::NONE)
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                 .Padding(PaddingMode::NONE)));
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
 
     ASSERT_EQ(ErrorCode::OK,
               Begin(KeyPurpose::SIGN,
@@ -1147,7 +1996,8 @@
                                                  .RsaSigningKey(2048, 65537)
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .Digest(Digest::SHA_2_256 /* supported digest */)
-                                                 .Padding(PaddingMode::PKCS7)));
+                                                 .Padding(PaddingMode::PKCS7)
+                                                 .SetDefaultValidity()));
     ASSERT_EQ(
             ErrorCode::UNSUPPORTED_PADDING_MODE,
             Begin(KeyPurpose::SIGN,
@@ -1164,7 +2014,8 @@
                                                  .RsaSigningKey(2048, 65537)
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .Digest(Digest::NONE)
-                                                 .Padding(PaddingMode::RSA_PSS)));
+                                                 .Padding(PaddingMode::RSA_PSS)
+                                                 .SetDefaultValidity()));
     ASSERT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
               Begin(KeyPurpose::SIGN,
                     AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::RSA_PSS)));
@@ -1174,7 +2025,7 @@
 }
 
 /*
- * SigningOperationsTest.RsaPssNoDigest
+ * 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.
@@ -1185,7 +2036,8 @@
                                                  .RsaKey(2048, 65537)
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .SigningKey()
-                                                 .Digest(Digest::NONE)));
+                                                 .Digest(Digest::NONE)
+                                                 .SetDefaultValidity()));
     ASSERT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE,
               Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Digest(Digest::NONE)));
 }
@@ -1200,7 +2052,8 @@
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaSigningKey(2048, 65537)
                                                  .Digest(Digest::NONE)
-                                                 .Padding(PaddingMode::NONE)));
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
 
     // Barely shorter
     string message(2048 / 8 - 1, 'a');
@@ -1221,7 +2074,8 @@
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaEncryptionKey(2048, 65537)
                                                  .Digest(Digest::NONE)
-                                                 .Padding(PaddingMode::NONE)));
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
     ASSERT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
               Begin(KeyPurpose::SIGN,
                     AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
@@ -1238,7 +2092,8 @@
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaSigningKey(2048, 65537)
                                                  .Digest(Digest::NONE)
-                                                 .Padding(PaddingMode::NONE)));
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
 
     // Largest possible message will always be larger than the public modulus.
     string message(2048 / 8, static_cast<char>(0xff));
@@ -1261,7 +2116,8 @@
             ErrorCode error = GenerateKey(AuthorizationSetBuilder()
                                                   .Authorization(TAG_NO_AUTH_REQUIRED)
                                                   .EcdsaSigningKey(key_size)
-                                                  .Digest(digest));
+                                                  .Digest(digest)
+                                                  .SetDefaultValidity());
             EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with size " << key_size
                                             << " and digest " << digest;
             if (error != ErrorCode::OK) continue;
@@ -1284,7 +2140,8 @@
         ErrorCode error = GenerateKey(AuthorizationSetBuilder()
                                               .Authorization(TAG_NO_AUTH_REQUIRED)
                                               .EcdsaSigningKey(curve)
-                                              .Digest(Digest::SHA_2_256));
+                                              .Digest(Digest::SHA_2_256)
+                                              .SetDefaultValidity());
         EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
         if (error != ErrorCode::OK) continue;
 
@@ -1306,7 +2163,8 @@
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .EcdsaSigningKey(256)
-                                                 .Digest(Digest::NONE)));
+                                                 .Digest(Digest::NONE)
+                                                 .SetDefaultValidity()));
     string message(1 * 1024, 'a');
     SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE));
 }
@@ -1322,7 +2180,8 @@
                                                  .EcdsaSigningKey(256)
                                                  .Digest(Digest::NONE)
                                                  .Authorization(TAG_APPLICATION_ID, "clientid")
-                                                 .Authorization(TAG_APPLICATION_DATA, "appdata")));
+                                                 .Authorization(TAG_APPLICATION_DATA, "appdata")
+                                                 .SetDefaultValidity()));
     EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
               Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Digest(Digest::NONE)));
     AbortIfNeeded();
@@ -1511,7 +2370,8 @@
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaSigningKey(2048, 65537)
                                                  .Digest(Digest::NONE)
-                                                 .Padding(PaddingMode::NONE)));
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
     string message = "12345678901234567890123456789012";
     string signature = SignMessage(
             message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
@@ -1520,7 +2380,7 @@
 }
 
 /*
- * VerificationOperationsTest.RsaSuccess
+ * VerificationOperationsTest.RsaAllPaddingsAndDigests
  *
  * Verifies RSA signature/verification for all padding modes and digests.
  */
@@ -1531,7 +2391,8 @@
                                   .Digest(ValidDigests(true /* withNone */, true /* withMD5 */))
                                   .Padding(PaddingMode::NONE)
                                   .Padding(PaddingMode::RSA_PSS)
-                                  .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
+                                  .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                                  .SetDefaultValidity();
 
     ASSERT_EQ(ErrorCode::OK, GenerateKey(authorizations));
 
@@ -1615,7 +2476,7 @@
 }
 
 /*
- * VerificationOperationsTest.RsaSuccess
+ * VerificationOperationsTest.RsaAllDigestsAndCurves
  *
  * Verifies ECDSA signature/verification for all digests and curves.
  */
@@ -1628,7 +2489,8 @@
         ErrorCode error = GenerateKey(AuthorizationSetBuilder()
                                               .Authorization(TAG_NO_AUTH_REQUIRED)
                                               .EcdsaSigningKey(curve)
-                                              .Digest(digests));
+                                              .Digest(digests)
+                                              .SetDefaultValidity());
         EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate key for EC curve " << curve;
         if (error != ErrorCode::OK) {
             continue;
@@ -1787,15 +2649,27 @@
  * 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(1024, 65537)
+                                               .RsaSigningKey(key_size, 65537)
                                                .Digest(Digest::SHA_2_256)
-                                               .Padding(PaddingMode::RSA_PSS),
-                                       KeyFormat::PKCS8, rsa_key));
+                                               .Padding(PaddingMode::RSA_PSS)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, key));
 
     CheckCryptoParam(TAG_ALGORITHM, Algorithm::RSA);
-    CheckCryptoParam(TAG_KEY_SIZE, 1024U);
+    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);
@@ -1818,7 +2692,8 @@
               ImportKey(AuthorizationSetBuilder()
                                 .RsaSigningKey(2048 /* Doesn't match key */, 65537)
                                 .Digest(Digest::NONE)
-                                .Padding(PaddingMode::NONE),
+                                .Padding(PaddingMode::NONE)
+                                .SetDefaultValidity(),
                         KeyFormat::PKCS8, rsa_key));
 }
 
@@ -1833,7 +2708,8 @@
               ImportKey(AuthorizationSetBuilder()
                                 .RsaSigningKey(1024, 3 /* Doesn't match key */)
                                 .Digest(Digest::NONE)
-                                .Padding(PaddingMode::NONE),
+                                .Padding(PaddingMode::NONE)
+                                .SetDefaultValidity(),
                         KeyFormat::PKCS8, rsa_key));
 }
 
@@ -1846,7 +2722,8 @@
     ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
                                                .Authorization(TAG_NO_AUTH_REQUIRED)
                                                .EcdsaSigningKey(256)
-                                               .Digest(Digest::SHA_2_256),
+                                               .Digest(Digest::SHA_2_256)
+                                               .SetDefaultValidity(),
                                        KeyFormat::PKCS8, ec_256_key));
 
     CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
@@ -1872,7 +2749,8 @@
     ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
                                                .Authorization(TAG_NO_AUTH_REQUIRED)
                                                .EcdsaSigningKey(256)
-                                               .Digest(Digest::SHA_2_256),
+                                               .Digest(Digest::SHA_2_256)
+                                               .SetDefaultValidity(),
                                        KeyFormat::PKCS8, ec_256_key_rfc5915));
 
     CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
@@ -1897,7 +2775,8 @@
     ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
                                                .Authorization(TAG_NO_AUTH_REQUIRED)
                                                .EcdsaSigningKey(256)
-                                               .Digest(Digest::SHA_2_256),
+                                               .Digest(Digest::SHA_2_256)
+                                               .SetDefaultValidity(),
                                        KeyFormat::PKCS8, ec_256_key_sec1));
 
     CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
@@ -1923,7 +2802,8 @@
     ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
                                                .Authorization(TAG_NO_AUTH_REQUIRED)
                                                .EcdsaSigningKey(521)
-                                               .Digest(Digest::SHA_2_256),
+                                               .Digest(Digest::SHA_2_256)
+                                               .SetDefaultValidity(),
                                        KeyFormat::PKCS8, ec_521_key));
 
     CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
@@ -1948,7 +2828,8 @@
     ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
               ImportKey(AuthorizationSetBuilder()
                                 .EcdsaSigningKey(224 /* Doesn't match key */)
-                                .Digest(Digest::NONE),
+                                .Digest(Digest::NONE)
+                                .SetDefaultValidity(),
                         KeyFormat::PKCS8, ec_256_key));
 }
 
@@ -1962,7 +2843,8 @@
     ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
               ImportKey(AuthorizationSetBuilder()
                                 .EcdsaSigningKey(EcCurve::P_224 /* Doesn't match key */)
-                                .Digest(Digest::NONE),
+                                .Digest(Digest::NONE)
+                                .SetDefaultValidity(),
                         KeyFormat::PKCS8, ec_256_key));
 }
 
@@ -1994,7 +2876,72 @@
 }
 
 /*
- * ImportKeyTest.AesSuccess
+ * 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}) {
+        ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                  ImportKey(AuthorizationSetBuilder()
+                                    .Authorization(TAG_NO_AUTH_REQUIRED)
+                                    .AesEncryptionKey(key_size)
+                                    .EcbMode()
+                                    .Padding(PaddingMode::PKCS7),
+                            KeyFormat::RAW, 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() * 8;
+    for (uint32_t key_size : {bitlen - 1, bitlen + 1, bitlen - 8, bitlen + 8}) {
+        ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                  ImportKey(AuthorizationSetBuilder()
+                                    .Authorization(TAG_NO_AUTH_REQUIRED)
+                                    .TripleDesEncryptionKey(key_size)
+                                    .EcbMode()
+                                    .Padding(PaddingMode::PKCS7),
+                            KeyFormat::RAW, key));
+    }
+}
+
+/*
+ * ImportKeyTest.HmacKeySuccess
  *
  * Verifies that importing and using an HMAC key works.
  */
@@ -2083,7 +3030,8 @@
                                      .RsaEncryptionKey(2048, 65537)
                                      .Digest(Digest::SHA_2_256)
                                      .Padding(PaddingMode::RSA_OAEP)
-                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY);
+                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
+                                     .SetDefaultValidity();
 
     ASSERT_EQ(ErrorCode::OK,
               ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
@@ -2103,7 +3051,8 @@
                                      .RsaEncryptionKey(2048, 65537)
                                      .Digest(Digest::SHA_2_256)
                                      .Padding(PaddingMode::RSA_OAEP)
-                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY);
+                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
+                                     .SetDefaultValidity();
 
     ASSERT_EQ(ErrorCode::OK,
               ImportWrappedKey(wrapped_key_masked, wrapping_key, wrapping_key_desc, masking_key,
@@ -2117,7 +3066,8 @@
                                      .RsaEncryptionKey(2048, 65537)
                                      .Digest(Digest::SHA_2_256)
                                      .Padding(PaddingMode::RSA_OAEP)
-                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY);
+                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
+                                     .SetDefaultValidity();
 
     ASSERT_EQ(
             ErrorCode::VERIFICATION_FAILED,
@@ -2131,7 +3081,8 @@
     auto wrapping_key_desc = AuthorizationSetBuilder()
                                      .RsaEncryptionKey(2048, 65537)
                                      .Digest(Digest::SHA_2_256)
-                                     .Padding(PaddingMode::RSA_OAEP);
+                                     .Padding(PaddingMode::RSA_OAEP)
+                                     .SetDefaultValidity();
 
     ASSERT_EQ(
             ErrorCode::INCOMPATIBLE_PURPOSE,
@@ -2154,7 +3105,8 @@
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaEncryptionKey(2048, 65537)
-                                                 .Padding(PaddingMode::NONE)));
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
 
     string message = string(2048 / 8, 'a');
     auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
@@ -2177,7 +3129,8 @@
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaEncryptionKey(2048, 65537)
-                                                 .Padding(PaddingMode::NONE)));
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
 
     string message = "1";
     auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
@@ -2206,7 +3159,8 @@
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaEncryptionKey(2048, 65537)
-                                                 .Padding(PaddingMode::NONE)));
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
 
     string message(2048 / 8 + 1, 'a');
 
@@ -2239,7 +3193,8 @@
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaEncryptionKey(key_size, 65537)
                                                  .Padding(PaddingMode::RSA_OAEP)
-                                                 .Digest(digests)));
+                                                 .Digest(digests)
+                                                 .SetDefaultValidity()));
 
     string message = "Hello";
 
@@ -2287,7 +3242,8 @@
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaEncryptionKey(2048, 65537)
                                                  .Padding(PaddingMode::RSA_OAEP)
-                                                 .Digest(Digest::NONE)));
+                                                 .Digest(Digest::NONE)
+                                                 .SetDefaultValidity()));
     string message = "Hello World!";
 
     auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_OAEP).Digest(Digest::NONE);
@@ -2295,7 +3251,7 @@
 }
 
 /*
- * EncryptionOperationsTest.RsaOaepInvalidDigest
+ * EncryptionOperationsTest.RsaOaepDecryptWithWrongDigest
  *
  * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to decrypt
  * with a different digest than was used to encrypt.
@@ -2307,7 +3263,8 @@
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaEncryptionKey(1024, 65537)
                                                  .Padding(PaddingMode::RSA_OAEP)
-                                                 .Digest(Digest::SHA_2_224, Digest::SHA_2_256)));
+                                                 .Digest(Digest::SHA_2_224, Digest::SHA_2_256)
+                                                 .SetDefaultValidity()));
     string message = "Hello World!";
     string ciphertext = EncryptMessage(
             message,
@@ -2332,7 +3289,8 @@
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaEncryptionKey(2048, 65537)
                                                  .Padding(PaddingMode::RSA_OAEP)
-                                                 .Digest(Digest::SHA_2_256)));
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .SetDefaultValidity()));
     constexpr size_t digest_size = 256 /* SHA_2_256 */ / 8;
     constexpr size_t oaep_overhead = 2 * digest_size + 2;
     string message(2048 / 8 - oaep_overhead + 1, 'a');
@@ -2360,7 +3318,8 @@
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaEncryptionKey(key_size, 65537)
                                                  .Padding(PaddingMode::RSA_OAEP)
-                                                 .Digest(Digest::SHA_2_256)));
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .SetDefaultValidity()));
 
     string message = "Hello";
 
@@ -2413,7 +3372,8 @@
                                   .Authorization(TAG_NO_AUTH_REQUIRED)
                                   .RsaEncryptionKey(2048, 65537)
                                   .Padding(PaddingMode::RSA_OAEP)
-                                  .Digest(Digest::SHA_2_256)));
+                                  .Digest(Digest::SHA_2_256)
+                                  .SetDefaultValidity()));
     string message = "Hello World!";
 
     auto params = AuthorizationSetBuilder()
@@ -2436,7 +3396,8 @@
                                   .Authorization(TAG_NO_AUTH_REQUIRED)
                                   .RsaEncryptionKey(2048, 65537)
                                   .Padding(PaddingMode::RSA_OAEP)
-                                  .Digest(Digest::SHA_2_256)));
+                                  .Digest(Digest::SHA_2_256)
+                                  .SetDefaultValidity()));
     string message = "Hello World!";
 
     auto params = AuthorizationSetBuilder()
@@ -2455,7 +3416,8 @@
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaEncryptionKey(2048, 65537)
-                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT)));
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT)
+                                                 .SetDefaultValidity()));
 
     string message = "Hello World!";
     auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT);
@@ -2494,7 +3456,8 @@
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaEncryptionKey(2048, 65537)
-                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT)));
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT)
+                                                 .SetDefaultValidity()));
     string message(2048 / 8 - 10, 'a');
 
     auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT);
@@ -2514,7 +3477,8 @@
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .EcdsaSigningKey(256)
-                                                 .Digest(Digest::NONE)));
+                                                 .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));
@@ -2571,7 +3535,7 @@
 }
 
 /*
- * EncryptionOperationsTest.AesEcbRoundTripSuccess
+ * EncryptionOperationsTest.AesWrongMode
  *
  * Verifies that AES encryption fails in the correct way when an unauthorized mode is specified.
  */
@@ -2801,39 +3765,22 @@
     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)
-                                  .Authorization(TAG_MAC_LENGTH, 128) /* for GCM */;
+            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;
-            int32_t input_consumed;
             string to_send;
             for (size_t i = 0; i < message.size(); i += increment) {
-                to_send.append(message.substr(i, increment));
-                EXPECT_EQ(ErrorCode::OK, Update(to_send, &ciphertext, &input_consumed));
-                EXPECT_EQ(to_send.length(), input_consumed);
-                to_send = to_send.substr(input_consumed);
-                EXPECT_EQ(0U, to_send.length());
-
-                switch (block_mode) {
-                    case BlockMode::ECB:
-                    case BlockMode::CBC:
-                        // Implementations must take as many blocks as possible, leaving less
-                        // than a block.
-                        EXPECT_LE(to_send.length(), 16U);
-                        break;
-                    case BlockMode::GCM:
-                    case BlockMode::CTR:
-                        // Implementations must always take all the data.
-                        EXPECT_EQ(0U, to_send.length());
-                        break;
-                }
+                EXPECT_EQ(ErrorCode::OK, Update(message.substr(i, increment), &ciphertext));
             }
-            EXPECT_EQ(ErrorCode::OK, Finish(to_send, &ciphertext)) << "Error sending " << to_send;
+            EXPECT_EQ(ErrorCode::OK, Finish(to_send, &ciphertext))
+                    << "Error sending " << to_send << " with block mode " << block_mode;
 
             switch (block_mode) {
                 case BlockMode::GCM:
@@ -2868,9 +3815,7 @@
 
             string plaintext;
             for (size_t i = 0; i < ciphertext.size(); i += increment) {
-                to_send.append(ciphertext.substr(i, increment));
-                EXPECT_EQ(ErrorCode::OK, Update(to_send, &plaintext, &input_consumed));
-                to_send = to_send.substr(input_consumed);
+                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
@@ -2990,7 +3935,7 @@
 }
 
 /*
- * EncryptionOperationsTest.AesCtrInvalidCallerNonce
+ * EncryptionOperationsTest.AesCbcRoundTripSuccess
  *
  * Verifies that keymint fails correctly when the user supplies an incorrect-size nonce.
  */
@@ -3127,17 +4072,13 @@
                                 .Padding(PaddingMode::NONE)
                                 .Authorization(TAG_MAC_LENGTH, 128);
 
-    auto update_params =
-            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
-
     // 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, Finish(update_params, message, "", &update_out_params, &ciphertext));
-
+    ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
+    ASSERT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
     ASSERT_EQ(ciphertext.length(), message.length() + 16);
 
     // Grab nonce
@@ -3145,12 +4086,9 @@
 
     // Decrypt.
     ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)) << "Begin decrypt";
+    ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
     string plaintext;
-    int32_t input_consumed;
-    ASSERT_EQ(ErrorCode::OK,
-              Update(update_params, ciphertext, &update_out_params, &plaintext, &input_consumed));
-    EXPECT_EQ(ciphertext.size(), input_consumed);
-    EXPECT_EQ(ErrorCode::OK, Finish("", &plaintext));
+    EXPECT_EQ(ErrorCode::OK, Finish(ciphertext, &plaintext));
     EXPECT_EQ(message.length(), plaintext.length());
     EXPECT_EQ(message, plaintext);
 }
@@ -3177,17 +4115,15 @@
                                 .Padding(PaddingMode::NONE)
                                 .Authorization(TAG_MAC_LENGTH, 128);
 
-    auto update_params =
-            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
-
     // 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(update_params, message, "", &update_out_params, &ciphertext));
+    ASSERT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
 
     ASSERT_EQ(ciphertext.length(), message.length() + 16);
 
@@ -3197,11 +4133,9 @@
     // Decrypt.
     ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)) << "Begin decrypt";
     string plaintext;
-    int32_t input_consumed;
+    ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
     sleep(5);
-    ASSERT_EQ(ErrorCode::OK,
-              Update(update_params, ciphertext, &update_out_params, &plaintext, &input_consumed));
-    EXPECT_EQ(ciphertext.size(), input_consumed);
+    ASSERT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext));
     sleep(5);
     EXPECT_EQ(ErrorCode::OK, Finish("", &plaintext));
     EXPECT_EQ(message.length(), plaintext.length());
@@ -3280,9 +4214,6 @@
                           .Padding(PaddingMode::NONE)
                           .Authorization(TAG_MAC_LENGTH, 128);
 
-    auto finish_params =
-            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
-
     // Encrypt
     AuthorizationSet begin_out_params;
     EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
@@ -3291,8 +4222,8 @@
 
     AuthorizationSet finish_out_params;
     string ciphertext;
-    EXPECT_EQ(ErrorCode::OK,
-              Finish(finish_params, message, "" /* signature */, &finish_out_params, &ciphertext));
+    ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
+    EXPECT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
 
     params = AuthorizationSetBuilder()
                      .Authorizations(begin_out_params)
@@ -3376,16 +4307,13 @@
                           .Padding(PaddingMode::NONE)
                           .Authorization(TAG_MAC_LENGTH, 128);
 
-    auto finish_params =
-            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
-
     // Encrypt
     AuthorizationSet begin_out_params;
     EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
     string ciphertext;
     AuthorizationSet finish_out_params;
-    EXPECT_EQ(ErrorCode::OK, Finish(finish_params, "" /* input */, "" /* signature */,
-                                    &finish_out_params, &ciphertext));
+    ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
+    EXPECT_EQ(ErrorCode::OK, Finish(&ciphertext));
     EXPECT_TRUE(finish_out_params.empty());
 
     // Grab nonce
@@ -3393,9 +4321,9 @@
 
     // Decrypt.
     EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+    ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
     string plaintext;
-    EXPECT_EQ(ErrorCode::OK, Finish(finish_params, ciphertext, "" /* signature */,
-                                    &finish_out_params, &plaintext));
+    EXPECT_EQ(ErrorCode::OK, Finish(ciphertext, &plaintext));
 
     EXPECT_TRUE(finish_out_params.empty());
 
@@ -3424,43 +4352,26 @@
                                 .Authorization(TAG_MAC_LENGTH, tag_bits);
     AuthorizationSet begin_out_params;
 
-    auto update_params =
-            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foo", (size_t)3);
-
     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;
-    int32_t input_consumed;
-    AuthorizationSet update_out_params;
-    EXPECT_EQ(ErrorCode::OK, Update(update_params, "" /* input */, &update_out_params, &ciphertext,
-                                    &input_consumed));
-    EXPECT_EQ(0U, input_consumed);
-    EXPECT_EQ(0U, ciphertext.size());
-    EXPECT_TRUE(update_out_params.empty());
+    EXPECT_EQ(ErrorCode::OK, Update(message, &ciphertext));
+    EXPECT_EQ(ErrorCode::OK, Finish(&ciphertext));
 
-    // AAD and data.
-    EXPECT_EQ(ErrorCode::OK,
-              Update(update_params, message, &update_out_params, &ciphertext, &input_consumed));
-    EXPECT_EQ(message.size(), input_consumed);
-    EXPECT_TRUE(update_out_params.empty());
-
-    EXPECT_EQ(ErrorCode::OK, Finish("" /* input */, &ciphertext));
     // Expect 128-bit (16-byte) tag appended to ciphertext.
-    EXPECT_EQ(message.size() + (tag_bits >> 3), ciphertext.size());
+    EXPECT_EQ(message.size() + (tag_bits / 8), ciphertext.size());
 
     // Grab nonce.
     begin_params.push_back(begin_out_params);
 
     // Decrypt
-    update_params =
-            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foofoo", (size_t)6);
-
     EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
+    EXPECT_EQ(ErrorCode::OK, UpdateAad("foofoo"));
     string plaintext;
-    EXPECT_EQ(ErrorCode::OK, Finish(update_params, ciphertext, "" /* signature */,
-                                    &update_out_params, &plaintext));
-    EXPECT_TRUE(update_out_params.empty());
+    EXPECT_EQ(ErrorCode::OK, Finish(ciphertext, &plaintext));
     EXPECT_EQ(message, plaintext);
 }
 
@@ -3484,32 +4395,14 @@
                                 .Authorization(TAG_MAC_LENGTH, 128);
     AuthorizationSet begin_out_params;
 
-    auto update_params =
-            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foo", (size_t)3);
-
     EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
 
-    // No data, AAD only.
+    EXPECT_EQ(ErrorCode::OK, UpdateAad("foo"));
     string ciphertext;
-    int32_t input_consumed;
-    AuthorizationSet update_out_params;
-    EXPECT_EQ(ErrorCode::OK, Update(update_params, "" /* input */, &update_out_params, &ciphertext,
-                                    &input_consumed));
-    EXPECT_EQ(0U, input_consumed);
-    EXPECT_EQ(0U, ciphertext.size());
-    EXPECT_TRUE(update_out_params.empty());
+    EXPECT_EQ(ErrorCode::OK, Update(message, &ciphertext));
+    EXPECT_EQ(ErrorCode::INVALID_TAG, UpdateAad("foo"));
 
-    // AAD and data.
-    EXPECT_EQ(ErrorCode::OK,
-              Update(update_params, message, &update_out_params, &ciphertext, &input_consumed));
-    EXPECT_EQ(message.size(), input_consumed);
-    EXPECT_TRUE(update_out_params.empty());
-
-    // More AAD
-    EXPECT_EQ(ErrorCode::INVALID_TAG,
-              Update(update_params, "", &update_out_params, &ciphertext, &input_consumed));
-
-    op_.reset();
+    op_ = {};
 }
 
 /*
@@ -3531,28 +4424,21 @@
                                 .Padding(PaddingMode::NONE)
                                 .Authorization(TAG_MAC_LENGTH, 128);
 
-    auto finish_params =
-            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foobar", (size_t)6);
-
     // 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(finish_params, message, "" /* signature */, &finish_out_params, &ciphertext));
+    EXPECT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
 
     // Grab nonce
     begin_params.push_back(begin_out_params);
 
-    finish_params = AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA,
-                                                            "barfoo" /* Wrong AAD */, (size_t)6);
-
     // 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(finish_params, ciphertext, "" /* signature */,
-                                                     &finish_out_params, &plaintext));
+    EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(ciphertext, &plaintext));
 }
 
 /*
@@ -3574,25 +4460,22 @@
                                 .Padding(PaddingMode::NONE)
                                 .Authorization(TAG_MAC_LENGTH, 128);
 
-    auto finish_params =
-            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foobar", (size_t)6);
-
     // 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(finish_params, message, "" /* signature */, &finish_out_params, &ciphertext));
+    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(finish_params, ciphertext, "" /* signature */,
-                                                     &finish_out_params, &plaintext));
+    EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(ciphertext, &plaintext));
 
     // With wrong nonce, should have gotten garbage plaintext (or none).
     EXPECT_NE(message, plaintext);
@@ -3619,17 +4502,12 @@
                           .Padding(PaddingMode::NONE)
                           .Authorization(TAG_MAC_LENGTH, 128);
 
-    auto finish_params =
-            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
-
     // Encrypt
     AuthorizationSet begin_out_params;
     EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
+    EXPECT_EQ(ErrorCode::OK, UpdateAad(aad));
     string ciphertext;
-    AuthorizationSet finish_out_params;
-    EXPECT_EQ(ErrorCode::OK,
-              Finish(finish_params, message, "" /* signature */, &finish_out_params, &ciphertext));
-    EXPECT_TRUE(finish_out_params.empty());
+    EXPECT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
 
     // Corrupt tag
     ++(*ciphertext.rbegin());
@@ -3639,10 +4517,9 @@
 
     // Decrypt.
     EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+    EXPECT_EQ(ErrorCode::OK, UpdateAad(aad));
     string plaintext;
-    EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(finish_params, ciphertext, "" /* signature */,
-                                                     &finish_out_params, &plaintext));
-    EXPECT_TRUE(finish_out_params.empty());
+    EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(ciphertext, &plaintext));
 }
 
 /*
@@ -3724,11 +4601,8 @@
                                                  .BlockMode(BlockMode::ECB)
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .Padding(PaddingMode::NONE)));
-    for (size_t i = 0; i < 32; ++i) {
-        auto inParams =
-                AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
-        EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, inParams));
-    }
+    auto inParams = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, inParams));
 }
 
 /*
@@ -3754,9 +4628,7 @@
     begin_params.push_back(TAG_PADDING, PaddingMode::PKCS7);
     EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
     string plaintext;
-    int32_t input_consumed;
-    EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext, &input_consumed));
-    EXPECT_EQ(ciphertext.size(), input_consumed);
+    EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext));
     EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, Finish(&plaintext));
 }
 
@@ -4070,9 +4942,7 @@
                                 .Authorization(TAG_NONCE, iv);
     EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
     string plaintext;
-    int32_t input_consumed;
-    EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext, &input_consumed));
-    EXPECT_EQ(ciphertext.size(), input_consumed);
+    EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext));
     EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, Finish(&plaintext));
 }
 
@@ -4096,10 +4966,8 @@
     EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, input_params, &output_params));
 
     string ciphertext;
-    int32_t input_consumed;
     for (size_t i = 0; i < message.size(); i += increment)
-        EXPECT_EQ(ErrorCode::OK,
-                  Update(message.substr(i, increment), &ciphertext, &input_consumed));
+        EXPECT_EQ(ErrorCode::OK, Update(message.substr(i, increment), &ciphertext));
     EXPECT_EQ(ErrorCode::OK, Finish(&ciphertext));
     EXPECT_EQ(message.size(), ciphertext.size());
 
@@ -4112,8 +4980,7 @@
     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, &input_consumed));
+        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);
@@ -4151,7 +5018,7 @@
 }
 
 /*
- * MaxOperationsTest.TestLimitAes
+ * MaxOperationsTest.TestLimitRsa
  *
  * Verifies that the max uses per boot tag works correctly with RSA keys.
  */
@@ -4162,7 +5029,8 @@
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaSigningKey(1024, 65537)
                                                  .NoDigestOrPadding()
-                                                 .Authorization(TAG_MAX_USES_PER_BOOT, 3)));
+                                                 .Authorization(TAG_MAX_USES_PER_BOOT, 3)
+                                                 .SetDefaultValidity()));
 
     string message = "1234567890123456";
 
@@ -4178,6 +5046,252 @@
 
 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;
 
 /*
@@ -4209,6 +5323,16 @@
     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;
@@ -4225,7 +5349,8 @@
                                      .Digest(Digest::NONE)
                                      .Padding(PaddingMode::NONE)
                                      .Authorization(TAG_NO_AUTH_REQUIRED)
-                                     .Authorization(TAG_ROLLBACK_RESISTANCE));
+                                     .Authorization(TAG_ROLLBACK_RESISTANCE)
+                                     .SetDefaultValidity());
     ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK);
 
     // Delete must work if rollback protection is implemented
@@ -4258,7 +5383,8 @@
                                      .Digest(Digest::NONE)
                                      .Padding(PaddingMode::NONE)
                                      .Authorization(TAG_NO_AUTH_REQUIRED)
-                                     .Authorization(TAG_ROLLBACK_RESISTANCE));
+                                     .Authorization(TAG_ROLLBACK_RESISTANCE)
+                                     .SetDefaultValidity());
     ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK);
 
     // Delete must work if rollback protection is implemented
@@ -4353,7 +5479,8 @@
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaEncryptionKey(2048, 65537)
-                                                 .Padding(PaddingMode::NONE)));
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
 
     auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
     constexpr size_t max_operations = 100;  // set to arbituary large number
@@ -4385,7 +5512,7 @@
 typedef KeyMintAidlTestBase TransportLimitTest;
 
 /*
- * TransportLimitTest.FinishInput
+ * TransportLimitTest.LargeFinishInput
  *
  * Verifies that passing input data to finish succeeds as expected.
  */
@@ -4424,17 +5551,244 @@
 
 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 EarlyBootKeyTest = KeyMintAidlTestBase;
+
+TEST_P(EarlyBootKeyTest, CreateEarlyBootKeys) {
+    auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
+            CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK);
+
+    CheckedDeleteKey(&aesKeyData.blob);
+    CheckedDeleteKey(&hmacKeyData.blob);
+    CheckedDeleteKey(&rsaKeyData.blob);
+    CheckedDeleteKey(&ecdsaKeyData.blob);
+}
+
+// This is a more comprenhensive 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 */, {} /* verificationToken */));
+    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") {
-                arm_deleteAllKeys = true;
+                aidl::android::hardware::security::keymint::test::KeyMintAidlTestBase::
+                        arm_deleteAllKeys = true;
             }
             if (std::string(argv[i]) == "--dump_attestations") {
-                dump_Attestations = true;
+                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;
             }
         }
     }
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
new file mode 100644
index 0000000..a2071c2
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -0,0 +1,601 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "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<EekChain> corrupt_sig_chain(const EekChain& eek, int which) {
+    auto [chain, _, parseErr] = cppbor::parse(eek.chain);
+    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 EekChain{corruptChain.encode(), eek.last_pubkey, eek.last_privkey};
+}
+
+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)) {
+        generateEek(3);
+    }
+
+    void generateEek(size_t eekLength) {
+        auto chain = generateEekChain(eekLength, eekId_);
+        EXPECT_TRUE(chain) << chain.message();
+        if (chain) eekChain_ = chain.moveValue();
+        eekLength_ = 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) {
+        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(eekChain_.last_pubkey, eekChain_.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(/* ignore_signature = */ false, signedMac->asArray(),
+                                              signingKey,
+                                              cppbor::Array()  // SignedMacAad
+                                                      .add(challenge_)
+                                                      .add(std::move(deviceInfoMap))
+                                                      .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();
+    }
+
+    bytevec eekId_;
+    size_t eekLength_;
+    EekChain eekChain_;
+    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);
+        generateEek(eekLength);
+
+        bytevec keysToSignMac;
+        DeviceInfo deviceInfo;
+        ProtectedData protectedData;
+        auto status = provisionable_->generateCertificateRequest(
+                testMode, {} /* keysToSign */, eekChain_.chain, challenge_, &deviceInfo,
+                &protectedData, &keysToSignMac);
+        ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+        checkProtectedData(deviceInfo, cppbor::Array(), keysToSignMac, protectedData);
+    }
+}
+
+/**
+ * Generate an empty certificate request in prod mode.  Generation will fail because we don't have a
+ * valid GEEK.
+ *
+ * TODO(swillden): Get a valid GEEK and use it so the generation can succeed, though we won't be
+ * able to decrypt.
+ */
+TEST_P(CertificateRequestTest, EmptyRequest_prodMode) {
+    bool testMode = false;
+    for (size_t eekLength : {2, 3, 7}) {
+        SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
+        generateEek(eekLength);
+
+        bytevec keysToSignMac;
+        DeviceInfo deviceInfo;
+        ProtectedData protectedData;
+        auto status = provisionable_->generateCertificateRequest(
+                testMode, {} /* keysToSign */, eekChain_.chain, challenge_, &deviceInfo,
+                &protectedData, &keysToSignMac);
+        EXPECT_FALSE(status.isOk());
+        EXPECT_EQ(status.getServiceSpecificError(),
+                  BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+    }
+}
+
+/**
+ * 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);
+        generateEek(eekLength);
+
+        bytevec keysToSignMac;
+        DeviceInfo deviceInfo;
+        ProtectedData protectedData;
+        auto status = provisionable_->generateCertificateRequest(
+                testMode, keysToSign_, eekChain_.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.  Must fail because we don't have a valid
+ * GEEK.
+ *
+ * TODO(swillden): Get a valid GEEK and use it so the generation can succeed, though we won't be
+ * able to decrypt.
+ */
+TEST_P(CertificateRequestTest, NonEmptyRequest_prodMode) {
+    bool testMode = false;
+    generateKeys(testMode, 4 /* numKeys */);
+
+    for (size_t eekLength : {2, 3, 7}) {
+        SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
+        generateEek(eekLength);
+
+        bytevec keysToSignMac;
+        DeviceInfo deviceInfo;
+        ProtectedData protectedData;
+        auto status = provisionable_->generateCertificateRequest(
+                testMode, keysToSign_, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
+                &keysToSignMac);
+        EXPECT_FALSE(status.isOk());
+        EXPECT_EQ(status.getServiceSpecificError(),
+                  BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+    }
+}
+
+/**
+ * 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}, eekChain_.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 = 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}, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
+            &keysToSignMac);
+    ASSERT_FALSE(status.isOk()) << status.getMessage();
+    auto rc = status.getServiceSpecificError();
+
+    // TODO(drysdale): drop the INVALID_EEK potential error code when a real GEEK is available.
+    EXPECT_TRUE(rc == BnRemotelyProvisionedComponent::STATUS_INVALID_EEK ||
+                rc == 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.
+ *
+ * TODO(drysdale): Update to use a valid GEEK, so that the test actually confirms that the
+ * implementation is checking signatures.
+ */
+TEST_P(CertificateRequestTest, NonEmptyCorruptEekRequest_prodMode) {
+    bool testMode = false;
+    generateKeys(testMode, 4 /* numKeys */);
+
+    for (size_t ii = 0; ii < eekLength_; ii++) {
+        auto chain = corrupt_sig_chain(eekChain_, ii);
+        ASSERT_TRUE(chain) << chain.message();
+        EekChain corruptEek = chain.moveValue();
+
+        bytevec keysToSignMac;
+        DeviceInfo deviceInfo;
+        ProtectedData protectedData;
+        auto status = provisionable_->generateCertificateRequest(
+                testMode, keysToSign_, corruptEek.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.
+ *
+ * TODO(drysdale): Update to use a valid GEEK, so that the test actually confirms that the
+ * implementation is checking signatures.
+ */
+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(eekChain_.chain);
+    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_, eekChain_.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_, eekChain_.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
index 0cfa798..718133a 100644
--- a/security/keymint/support/Android.bp
+++ b/security/keymint/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: "libkeymint_support",
     cflags: [
@@ -31,9 +40,25 @@
         "include",
     ],
     shared_libs: [
-        "android.hardware.security.keymint-unstable-ndk_platform",
+        "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: [
+        "libcppbor_external",
+        "libcppcose_rkp",
+        "libcrypto",
+    ],
+}
diff --git a/security/keymint/support/attestation_record.cpp b/security/keymint/support/attestation_record.cpp
index 596b097..a48f770 100644
--- a/security/keymint/support/attestation_record.cpp
+++ b/security/keymint/support/attestation_record.cpp
@@ -97,6 +97,7 @@
     ASN1_NULL* device_unique_attestation;
     ASN1_NULL* storage_key;
     ASN1_NULL* identity_credential;
+    ASN1_INTEGER* usage_count_limit;
 } KM_AUTH_LIST;
 
 ASN1_SEQUENCE(KM_AUTH_LIST) = {
@@ -143,7 +144,8 @@
         ASN1_EXP_OPT(KM_AUTH_LIST, storage_key, ASN1_NULL, TAG_STORAGE_KEY.maskedTag()),
         ASN1_EXP_OPT(KM_AUTH_LIST, identity_credential, ASN1_NULL,
                      TAG_IDENTITY_CREDENTIAL_KEY.maskedTag()),
-
+        ASN1_EXP_OPT(KM_AUTH_LIST, usage_count_limit, ASN1_INTEGER,
+                     TAG_USAGE_COUNT_LIMIT.maskedTag()),
 } ASN1_SEQUENCE_END(KM_AUTH_LIST);
 IMPLEMENT_ASN1_FUNCTIONS(KM_AUTH_LIST);
 
@@ -285,6 +287,7 @@
     copyAuthTag(record->device_unique_attestation, TAG_DEVICE_UNIQUE_ATTESTATION, auth_list);
     copyAuthTag(record->storage_key, TAG_STORAGE_KEY, auth_list);
     copyAuthTag(record->identity_credential, TAG_IDENTITY_CREDENTIAL_KEY, auth_list);
+    copyAuthTag(record->usage_count_limit, TAG_USAGE_COUNT_LIMIT, auth_list);
 
     return ErrorCode::OK;
 }
diff --git a/security/keymint/support/authorization_set.cpp b/security/keymint/support/authorization_set.cpp
index 3d44dff..25eace3 100644
--- a/security/keymint/support/authorization_set.cpp
+++ b/security/keymint/support/authorization_set.cpp
@@ -191,6 +191,10 @@
     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);
@@ -243,4 +247,12 @@
     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/authorization_set.h b/security/keymint/support/include/keymint_support/authorization_set.h
index 1407c5f..ca51b08 100644
--- a/security/keymint/support/include/keymint_support/authorization_set.h
+++ b/security/keymint/support/include/keymint_support/authorization_set.h
@@ -288,6 +288,7 @@
 
     AuthorizationSetBuilder& SigningKey();
     AuthorizationSetBuilder& EncryptionKey();
+    AuthorizationSetBuilder& AttestKey();
 
     AuthorizationSetBuilder& NoDigestOrPadding();
 
@@ -300,6 +301,8 @@
     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);
     }
diff --git a/security/keymint/support/include/keymint_support/keymint_tags.h b/security/keymint/support/include/keymint_support/keymint_tags.h
index 76aecb7..5b2a6f3 100644
--- a/security/keymint/support/include/keymint_support/keymint_tags.h
+++ b/security/keymint/support/include/keymint_support/keymint_tags.h
@@ -119,12 +119,18 @@
 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
 
@@ -135,19 +141,19 @@
         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_ATTESTATION_ID_SERIAL_t, TAG_ATTESTATION_ID_IMEI_t, TAG_ATTESTATION_ID_MEID_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_TRUSTED_USER_PRESENCE_REQUIRED_t, TAG_CERTIFICATE_SERIAL_t, TAG_CERTIFICATE_SUBJECT_t>;
 
 template <typename TypedTagType>
 struct TypedTag2ValueType;
@@ -325,7 +331,9 @@
 inline std::optional<
         std::reference_wrapper<const typename TypedTag2ValueType<TypedTag<tag_type, tag>>::type>>
 authorizationValue(TypedTag<tag_type, tag> ttag, const KeyParameter& param) {
-    if (TypedTag2ValueType<TypedTag<tag_type, tag>>::unionTag != param.value.getTag()) return {};
+    // 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);
 }
 
diff --git a/security/keymint/support/include/keymint_support/keymint_utils.h b/security/keymint/support/include/keymint_support/keymint_utils.h
index 53d5b96..e1ead21 100644
--- a/security/keymint/support/include/keymint_support/keymint_utils.h
+++ b/security/keymint/support/include/keymint_support/keymint_utils.h
@@ -38,5 +38,6 @@
 
 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
index 9ae7e52..dee28ba 100644
--- a/security/keymint/support/include/keymint_support/openssl_utils.h
+++ b/security/keymint/support/include/keymint_support/openssl_utils.h
@@ -34,10 +34,15 @@
     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(BN_CTX)
+MAKE_OPENSSL_PTR_TYPE(X509_NAME)
 
 typedef std::unique_ptr<BIGNUM, UniquePtrDeleter<BIGNUM, BN_free>> BIGNUM_Ptr;
 
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..e4261f3
--- /dev/null
+++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+/**
+ * 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);
+
+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);
+
+}  // namespace aidl::android::hardware::security::keymint::remote_prov
diff --git a/security/keymint/support/keymint_utils.cpp b/security/keymint/support/keymint_utils.cpp
index e73d602..2dbdfa8 100644
--- a/security/keymint/support/keymint_utils.cpp
+++ b/security/keymint/support/keymint_utils.cpp
@@ -31,10 +31,11 @@
 constexpr size_t kPlatformVersionMatchCount = kSubminorVersionMatch + 1;
 
 constexpr char kPlatformPatchlevelProp[] = "ro.build.version.security_patch";
-constexpr char kPlatformPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-[0-9]{2}$";
+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 kPlatformPatchlevelMatchCount = kMonthMatch + 1;
+constexpr size_t kPatchlevelMatchCount = kMonthMatch + 1;
 
 uint32_t match_to_uint32(const char* expression, const regmatch_t& match) {
     if (match.rm_so == -1) return 0;
@@ -80,15 +81,14 @@
     return getOsVersion(version.c_str());
 }
 
-uint32_t getOsPatchlevel(const char* patchlevel_str) {
+uint32_t getPatchlevel(const char* patchlevel_str) {
     regex_t regex;
-    if (regcomp(&regex, kPlatformPatchlevelRegex, REG_EXTENDED) != 0) {
+    if (regcomp(&regex, kPatchlevelRegex, REG_EXTENDED) != 0) {
         return 0;
     }
 
-    regmatch_t matches[kPlatformPatchlevelMatchCount];
-    int not_match =
-            regexec(&regex, patchlevel_str, kPlatformPatchlevelMatchCount, matches, 0 /* flags */);
+    regmatch_t matches[kPatchlevelMatchCount];
+    int not_match = regexec(&regex, patchlevel_str, kPatchlevelMatchCount, matches, 0 /* flags */);
     regfree(&regex);
     if (not_match) {
         return 0;
@@ -105,7 +105,12 @@
 
 uint32_t getOsPatchlevel() {
     std::string patchlevel = wait_and_get_property(kPlatformPatchlevelProp);
-    return getOsPatchlevel(patchlevel.c_str());
+    return getPatchlevel(patchlevel.c_str());
+}
+
+uint32_t getVendorPatchlevel() {
+    std::string patchlevel = wait_and_get_property(kVendorPatchlevelProp);
+    return getPatchlevel(patchlevel.c_str());
 }
 
 }  // 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..da10eb2
--- /dev/null
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -0,0 +1,171 @@
+/*
+ * 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 <remote_prov/remote_prov_utils.h>
+
+#include <openssl/rand.h>
+
+#include <cppbor.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) {
+    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};
+}
+
+ErrMsgOr<bytevec> verifyAndParseCoseSign1Cwt(bool ignoreSignature, 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";
+
+    if (!ignoreSignature) {
+        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(false /* ignoreSignature */, 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;
+}
+
+}  // namespace aidl::android::hardware::security::keymint::remote_prov
diff --git a/security/secureclock/aidl/Android.bp b/security/secureclock/aidl/Android.bp
index 5a6d7ae..c78be3b 100644
--- a/security/secureclock/aidl/Android.bp
+++ b/security/secureclock/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.security.secureclock",
     vendor_available: true,
@@ -7,7 +16,8 @@
     stability: "vintf",
     backend: {
         java: {
-            sdk_version: "module_current",
+            platform_apis: true,
+            srcs_available: true,
         },
         ndk: {
             vndk: {
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
index c16b312..4ecc1e4 100644
--- 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
@@ -1,3 +1,17 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,8 +31,9 @@
 // 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 = "Time Verification";
+  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
index 21eeb74..d105ac8 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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;
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
index f01fdc7..2e0e389 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +32,7 @@
 // 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
index 7d416dd..a742ff0 100644
--- a/security/secureclock/aidl/android/hardware/security/secureclock/ISecureClock.aidl
+++ b/security/secureclock/aidl/android/hardware/security/secureclock/ISecureClock.aidl
@@ -25,15 +25,15 @@
  * 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 = "Time Verification";
+    const String TIME_STAMP_MAC_LABEL = "Auth Verification";
 
     /**
      * Generates an authenticated timestamp.
diff --git a/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl b/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
index 3fb5860..2fbd29a 100644
--- a/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
+++ b/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
@@ -20,8 +20,8 @@
 
 /**
  * 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 {
@@ -39,18 +39,20 @@
      * 32-byte HMAC-SHA256 of the above values, computed as:
      *
      *    HMAC(H,
-     *         ISecureClock.TIME_STAMP_MAC_LABEL || challenge || timestamp)
+     *         ISecureClock.TIME_STAMP_MAC_LABEL || challenge || timestamp || securityLevel )
      *
      * where:
      *
-     *   ``ISecureClock.TIME_STAMP_MAC_LABEL'' is a sting constant defined in ISecureClock.aidl.
+     *   ``ISecureClock.TIME_STAMP_MAC_LABEL'' is a string constant defined in ISecureClock.aidl.
      *
-     *   ``H'' is the shared HMAC key (see computeSharedHmac() in ISharedHmacSecret).
+     *   ``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.  securityLevel is represented as a 32-bit unsigned integer in big-endian order.
+     * order. SecurityLevel is represented as a 32-bit unsigned integer in big-endian order as
+     * described in android.hardware.security.keymint.SecurityLevel. It represents the security
+     * level of the secure clock environment.
      */
     byte[] mac;
 }
diff --git a/security/secureclock/aidl/android/hardware/security/secureclock/Timestamp.aidl b/security/secureclock/aidl/android/hardware/security/secureclock/Timestamp.aidl
index 27758e1..5061aa4 100644
--- a/security/secureclock/aidl/android/hardware/security/secureclock/Timestamp.aidl
+++ b/security/secureclock/aidl/android/hardware/security/secureclock/Timestamp.aidl
@@ -21,6 +21,7 @@
  * 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)
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..31f4854
--- /dev/null
+++ b/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp
@@ -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.
+ */
+#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);
+
+    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);
+
+    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);
+
+    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);
+
+    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
index ab44110..1683059 100644
--- a/security/sharedsecret/aidl/Android.bp
+++ b/security/sharedsecret/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.security.sharedsecret",
     vendor_available: 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
index 2509936..e76efe7 100644
--- 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
@@ -1,3 +1,17 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +31,7 @@
 // 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();
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
index 9b65046..e15fd49 100644
--- 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
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -17,7 +32,8 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.security.sharedsecret;
-@VintfStability
+/* @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
index 906303f..eca8d87 100644
--- a/security/sharedsecret/aidl/android/hardware/security/sharedsecret/ISharedSecret.aidl
+++ b/security/sharedsecret/aidl/android/hardware/security/sharedsecret/ISharedSecret.aidl
@@ -22,8 +22,8 @@
  * 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 two steps to establish a shared secret between
- * the collaborating services:
+ * 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
@@ -35,8 +35,8 @@
  * 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 {
     /**
@@ -64,11 +64,11 @@
 
     /**
      * 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 keymint services, and
-     * sends to it all of the SharedSecretParameters returned by all keymint services.
+     * 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 keymint services
-     * instances perform the same computation to arrive at the same result):
+     * 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,
@@ -98,16 +98,16 @@
      * 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 keymint services when getSharedSecretParameters
+     * SharedSecretParameters data returned by all HAL instances when getSharedSecretParameters
      * was called.
      *
-     * @return sharingCheck A 32-byte value used to verify that all the keymint services have
+     * @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
-     *         keymint services don't match, clients must assume that HMAC agreement
+     *         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
index 691b3f1..8144699 100644
--- a/security/sharedsecret/aidl/android/hardware/security/sharedsecret/SharedSecretParameters.aidl
+++ b/security/sharedsecret/aidl/android/hardware/security/sharedsecret/SharedSecretParameters.aidl
@@ -21,9 +21,10 @@
  * 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
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..8426120
--- /dev/null
+++ b/security/sharedsecret/aidl/vts/functional/SharedSecretAidlTest.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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);
+        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, 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.";
+        }
+    }
+}
+}  // 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 1093671..c81ee6d 100644
--- a/sensors/1.0/Android.bp
+++ b/sensors/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.sensors@1.0",
     root: "android.hardware",
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/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/2.0/Android.bp b/sensors/2.0/Android.bp
index d71f07b..5a22b1f 100644
--- a/sensors/2.0/Android.bp
+++ b/sensors/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.sensors@2.0",
     root: "android.hardware",
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/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/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 9ba3248..5dd511a 100644
--- a/sensors/2.1/Android.bp
+++ b/sensors/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.sensors@2.1",
     root: "android.hardware",
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 4c5386a..06446a2 100644
--- a/sensors/2.1/default/SensorsV2_1.cpp
+++ b/sensors/2.1/default/SensorsV2_1.cpp
@@ -46,7 +46,8 @@
         mSensorInfo.fifoMaxEventCount = 0;
         mSensorInfo.requiredPermission = "";
         mSensorInfo.flags = static_cast<uint32_t>(V1_0::SensorFlagBits::ON_CHANGE_MODE |
-                                                  V1_0::SensorFlagBits::WAKE_UP);
+                                                  V1_0::SensorFlagBits::WAKE_UP |
+                                                  V1_0::SensorFlagBits::DATA_INJECTION);
     }
 };
 
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/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 642fc89..1a7c628 100644
--- a/sensors/common/default/2.X/Sensor.cpp
+++ b/sensors/common/default/2.X/Sensor.cpp
@@ -313,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;
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/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/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 8cf5003..47a8cc0 100644
--- a/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
+++ b/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
@@ -462,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);
@@ -485,7 +486,6 @@
         ASSERT_EQ(lastEvent.u.vec3.status, injectedEvent.u.vec3.status);
     }
 
-    getEnvironment()->unregisterCallback();
     ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::NORMAL));
 }
 
@@ -603,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);
         }
     }
 
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/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.1/Android.bp b/soundtrigger/2.1/Android.bp
index 024e0f6..7f6503c 100644
--- a/soundtrigger/2.1/Android.bp
+++ b/soundtrigger/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.soundtrigger@2.1",
     root: "android.hardware",
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.2/Android.bp b/soundtrigger/2.2/Android.bp
index dbf4f8b..f126eed 100644
--- a/soundtrigger/2.2/Android.bp
+++ b/soundtrigger/2.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.soundtrigger@2.2",
     root: "android.hardware",
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.3/Android.bp b/soundtrigger/2.3/Android.bp
index 480df4d..e65c0ad 100644
--- a/soundtrigger/2.3/Android.bp
+++ b/soundtrigger/2.3/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.3",
     root: "android.hardware",
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/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/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/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/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..4d544e1 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
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/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/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/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/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/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.1/Android.bp b/tests/lazy/1.1/Android.bp
index ccedd8d..9429a23 100644
--- a/tests/lazy/1.1/Android.bp
+++ b/tests/lazy/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.tests.lazy@1.1",
     root: "android.hardware",
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/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/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/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/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/default/Android.bp b/tests/msgq/1.0/default/Android.bp
index a50206b..5f116e7 100644
--- a/tests/msgq/1.0/default/Android.bp
+++ b/tests/msgq/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.tests.msgq@1.0-impl",
     defaults: ["hidl_defaults"],
@@ -91,8 +100,8 @@
     // These are static libs only for testing purposes and portability. Shared
     // libs should be used on device.
     static_libs: [
-        "android.hardware.common-unstable-ndk_platform",
-        "android.hardware.common.fmq-unstable-ndk_platform",
+        "android.hardware.common-V2-ndk_platform",
+        "android.hardware.common.fmq-V1-ndk_platform",
         "android.hardware.tests.msgq@1.0",
         "android.fmq.test-ndk_platform",
     ],
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/Android.bp b/tests/safeunion/1.0/Android.bp
index f1ec267..ca1edfc 100644
--- a/tests/safeunion/1.0/Android.bp
+++ b/tests/safeunion/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@1.0",
     root: "android.hardware",
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/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/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 e774048..116c9b6 100644
--- a/tetheroffload/config/1.0/Android.bp
+++ b/tetheroffload/config/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.tetheroffload.config@1.0",
     root: "android.hardware",
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/control/1.0/Android.bp b/tetheroffload/control/1.0/Android.bp
index 4bcaed2..acb5ee8 100644
--- a/tetheroffload/control/1.0/Android.bp
+++ b/tetheroffload/control/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.tetheroffload.control@1.0",
     root: "android.hardware",
diff --git a/tetheroffload/control/1.0/vts/functional/Android.bp b/tetheroffload/control/1.0/vts/functional/Android.bp
index c397df4..dc3b00c 100644
--- a/tetheroffload/control/1.0/vts/functional/Android.bp
+++ b/tetheroffload/control/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: "VtsHalTetheroffloadControlV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -30,3 +39,17 @@
         "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
index bd0dad7..e392e96 100644
--- a/tetheroffload/control/1.0/vts/functional/OffloadControlTestBase.cpp
+++ b/tetheroffload/control/1.0/vts/functional/OffloadControlTestBase.cpp
@@ -53,29 +53,6 @@
     ASSERT_TRUE(ret.isOk());
 }
 
-void OffloadControlTestBase::prepareControlHal() {
-    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";
-}
-
-void OffloadControlTestBase::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 OffloadControlTestBase::setupControlHal() {
-    prepareControlHal();
-    initOffload(true);
-}
-
 void OffloadControlTestBase::stopOffload(const ExpectBoolean value) {
     auto cb = [&](bool success, const hidl_string& errMsg) {
         switch (value) {
diff --git a/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestBase.h b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestBase.h
index 004019a..994c808 100644
--- a/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestBase.h
+++ b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestBase.h
@@ -36,7 +36,6 @@
 using android::hardware::Return;
 using android::hardware::Void;
 using android::hardware::tetheroffload::config::V1_0::IOffloadConfig;
-using android::hardware::tetheroffload::control::V1_0::IOffloadControl;
 using android::hardware::tetheroffload::control::V1_0::ITetheringOffloadCallback;
 using android::hardware::tetheroffload::control::V1_0::NatTimeoutUpdate;
 using android::hardware::tetheroffload::control::V1_0::OffloadCallbackEvent;
@@ -64,17 +63,21 @@
 
     // Called once in setup stage to retrieve correct version of
     // IOffloadControl object.
-    virtual sp<IOffloadControl> createControl(const std::string& serviceName) = 0;
+    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();
 
-    void prepareControlHal();
+    virtual void prepareControlHal() = 0;
 
-    void initOffload(const bool expected_result);
+    virtual void initOffload(const bool expected_result) = 0;
 
-    void setupControlHal();
+    void setupControlHal() {
+        prepareControlHal();
+        initOffload(true);
+    };
 
     void stopOffload(const ExpectBoolean value);
 
@@ -100,6 +103,6 @@
     };
 
     sp<IOffloadConfig> config;
-    sp<IOffloadControl> control;
+    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/OffloadControlTestV1_0.h b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestV1_0.h
index 7492f8a..9189d71 100644
--- a/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestV1_0.h
+++ b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestV1_0.h
@@ -26,8 +26,28 @@
         prepareControlHal();
     }
 
-    virtual sp<IOffloadControl> createControl(const std::string& serviceName) override {
-        return IOffloadControl::getService(serviceName);
+    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());
     }
 };
 
diff --git a/tetheroffload/control/1.1/Android.bp b/tetheroffload/control/1.1/Android.bp
new file mode 100644
index 0000000..e87ff5c
--- /dev/null
+++ b/tetheroffload/control/1.1/Android.bp
@@ -0,0 +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.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",
+    ],
+    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..7a7d56d
--- /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. Events which are common for 1.0 and 1.1 HAL
+     * MUST be fired on both 1.0 and 1.1 callback.
+     */
+    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 10eeddc..f6cf294 100644
--- a/thermal/1.0/Android.bp
+++ b/thermal/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.thermal@1.0",
     root: "android.hardware",
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.1/Android.bp b/thermal/1.1/Android.bp
index 7dc30a3..ef2fa20 100644
--- a/thermal/1.1/Android.bp
+++ b/thermal/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.thermal@1.1",
     root: "android.hardware",
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/2.0/Android.bp b/thermal/2.0/Android.bp
index 3d9cea1..eb47b38 100644
--- a/thermal/2.0/Android.bp
+++ b/thermal/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.thermal@2.0",
     root: "android.hardware",
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/tv/cec/1.0/Android.bp b/tv/cec/1.0/Android.bp
index 0e0f284..889399a 100644
--- a/tv/cec/1.0/Android.bp
+++ b/tv/cec/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.tv.cec@1.0",
     root: "android.hardware",
diff --git a/tv/cec/1.0/default/Android.bp b/tv/cec/1.0/default/Android.bp
index 239a527..fc4298d 100644
--- a/tv/cec/1.0/default/Android.bp
+++ b/tv/cec/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.cec@1.0-impl",
     defaults: ["hidl_defaults"],
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 5463b6d..0000000
--- a/tv/cec/2.0/Android.bp
+++ /dev/null
@@ -1,16 +0,0 @@
-// This file is autogenerated by hidl-gen -Landroidbp.
-
-hidl_interface {
-    name: "android.hardware.tv.cec@2.0",
-    root: "android.hardware",
-    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 1121f4e..292ec63 100644
--- a/tv/input/1.0/Android.bp
+++ b/tv/input/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.tv.input@1.0",
     root: "android.hardware",
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/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 8092d5e..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));
 }
 
 /*
diff --git a/tv/tuner/1.0/Android.bp b/tv/tuner/1.0/Android.bp
index e578641..237f495 100644
--- a/tv/tuner/1.0/Android.bp
+++ b/tv/tuner/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.tv.tuner@1.0",
     root: "android.hardware",
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/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/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 22ba271..62093cc 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);
 }
 
@@ -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));
@@ -75,26 +74,20 @@
 
 void TunerBroadcastHidlTest::broadcastSingleFilterTest(FilterConfig filterConf,
                                                        FrontendConfig frontendConf) {
-    if (!frontendConf.enable) {
-        return;
-    }
     uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
     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);
@@ -106,7 +99,7 @@
     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));
@@ -117,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));
@@ -152,7 +147,7 @@
     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());
@@ -163,27 +158,36 @@
 
 void TunerRecordHidlTest::recordSingleFilterTest(FilterConfig filterConf,
                                                  FrontendConfig frontendConf, DvrConfig dvrConf) {
-    if (!frontendConf.enable) {
-        return;
-    }
-    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());
@@ -197,34 +201,61 @@
     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;
@@ -233,23 +264,28 @@
 void TunerRecordHidlTest::attachSingleFilterToRecordDvrTest(FilterConfig filterConf,
                                                             FrontendConfig frontendConf,
                                                             DvrConfig dvrConf) {
-    uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+
+    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));
+    }
+
     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));
@@ -265,36 +301,42 @@
     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) {
-    if (!frontendConf.enable) {
-        return;
-    }
-    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];
+        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));
@@ -305,7 +347,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++) {
@@ -317,10 +359,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));
     }
@@ -331,59 +389,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[defaultFrontend]);
+    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[defaultScanFrontend], 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[defaultScanFrontend], 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[defaultFrontend].type, feId);
+    mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
     ASSERT_TRUE(feId != INVALID_ID);
     ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
     ASSERT_TRUE(mFrontendTests.setFrontendCallback());
@@ -395,6 +467,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;
@@ -403,22 +481,22 @@
     uint32_t avSyncHwId;
     sp<IFilter> mediaFilter;
 
-    mFrontendTests.getFrontendIdByType(frontendArray[defaultFrontend].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));
@@ -430,8 +508,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[defaultFrontend]);
+    configSingleFilterInDemuxTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerFilterHidlTest, SetFilterLinkage) {
@@ -448,11 +529,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));
@@ -466,81 +545,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[defaultFrontend]);
+    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[defaultFrontend]);
+    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[defaultFrontend]);
+    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[defaultFrontend]);
+    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[defaultFrontend],
-                                      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[defaultFrontend],
-                           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[defaultFrontend].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[defaultFrontend], 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 5a23ca5..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);
     }
@@ -75,7 +86,7 @@
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mLnbTests.setService(mService);
     }
@@ -97,7 +108,7 @@
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -123,7 +134,7 @@
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -138,6 +149,29 @@
     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;
@@ -152,7 +186,7 @@
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -173,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,
@@ -191,7 +225,7 @@
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -210,7 +244,7 @@
     FilterTests mFilterTests;
     DvrTests mDvrTests;
 
-    AssertionResult filterDataOutputTest(vector<string> goldenOutputFiles);
+    AssertionResult filterDataOutputTest();
 
     void playbackSingleFilterTest(FilterConfig filterConf, DvrConfig dvrConf);
 };
@@ -223,7 +257,7 @@
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -265,7 +299,7 @@
         mCasService = IMediaCasService::getService();
         ASSERT_NE(mService, nullptr);
         ASSERT_NE(mCasService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -281,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;
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
index 92a8130..735bc82 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
@@ -21,372 +21,256 @@
 #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_512K = 0x80000;
-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 getMqDesc;
-
-    bool operator<(const FilterConfig& /*c*/) const { return false; }
-};
-
-struct TimeFilterConfig {
-    bool supportTimeFilter;
-    uint64_t timeStamp;
-};
-
-struct FrontendConfig {
-    bool enable;
-    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;
-static int defaultFrontend = DVBT;
-static int defaultScanFrontend = SCAN_DVBT;
-
-/** 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].enable = true;
-    frontendArray[DVBS].type = FrontendType::DVBS;
-    frontendArray[DVBS].enable = true;
-    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,
-    });
-    filterArray[TS_PES0].getMqDesc = true;
-    // 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,
-    });
-    filterArray[TS_SECTION0].getMqDesc = true;
-    // 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 = filterMap.find(live.audioFilterId) != filterMap.end() &&
+                         filterMap.find(live.videoFilterId) != filterMap.end();
+    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/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..7971fd2
--- /dev/null
+++ b/tv/tuner/config/TunerTestingConfigReader.h
@@ -0,0 +1,772 @@
+/*
+ * 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::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);
+                } else {
+                    descramblerMap[id].hidlPvtData.resize(256);
+                }
+            }
+        }
+    }
+
+    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.frontendId = recordConfig.getFrontendConnection();
+        record.recordFilterId = recordConfig.getRecordFilterConnection();
+        record.dvrRecordId = recordConfig.getDvrRecordConnection();
+        if (recordConfig.hasDvrSoftwareFeConnection()) {
+            record.dvrSoftwareFeId = recordConfig.getDvrSoftwareFeConnection();
+        }
+        if (recordConfig.getHasFrontendConnection()) {
+            record.hasFrontendConnection = true;
+            record.dvrSourceId = emptyHardwareId;
+        } 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.frontendId = descConfig.getFrontendConnection();
+        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;
+        } 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.isHighPriority =
+                feConfig.getFirstDvbtFrontendSettings_optional()->getIsHighPriority();
+        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..abd7155
--- /dev/null
+++ b/tv/tuner/config/api/current.txt
@@ -0,0 +1,441 @@
+// 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 getIsHighPriority();
+    method @Nullable public java.math.BigInteger getTransmissionMode();
+    method public void setBandwidth(@Nullable java.math.BigInteger);
+    method public void setIsHighPriority(@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/tv/tuner/config/api/last_current.txt b/tv/tuner/config/api/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tv/tuner/config/api/last_current.txt
diff --git a/tv/tuner/config/api/last_removed.txt b/tv/tuner/config/api/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/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..2624076
--- /dev/null
+++ b/tv/tuner/config/sample_tuner_vts_config.xml
@@ -0,0 +1,226 @@
+<?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"/>
+            </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..e0c3e33
--- /dev/null
+++ b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
@@ -0,0 +1,658 @@
+<?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="transmissionMode" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="isHighPriority" 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/usb/1.0/Android.bp b/usb/1.0/Android.bp
index 607d1ac..d5d3500 100644
--- a/usb/1.0/Android.bp
+++ b/usb/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.usb@1.0",
     root: "android.hardware",
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.1/Android.bp b/usb/1.1/Android.bp
index 8742e77..9c3e0dc 100644
--- a/usb/1.1/Android.bp
+++ b/usb/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.usb@1.1",
     root: "android.hardware",
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.2/Android.bp b/usb/1.2/Android.bp
index 8b9f0fb..0d4430d 100644
--- a/usb/1.2/Android.bp
+++ b/usb/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.usb@1.2",
     root: "android.hardware",
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 21f152c..bb602fa 100644
--- a/usb/gadget/1.0/Android.bp
+++ b/usb/gadget/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.usb.gadget@1.0",
     root: "android.hardware",
diff --git a/usb/gadget/1.1/Android.bp b/usb/gadget/1.1/Android.bp
index e510069..388f11c 100644
--- a/usb/gadget/1.1/Android.bp
+++ b/usb/gadget/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.usb.gadget@1.1",
     root: "android.hardware",
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 d6321fb..1ae1cef 100644
--- a/vibrator/1.0/Android.bp
+++ b/vibrator/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.vibrator@1.0",
     root: "android.hardware",
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.1/Android.bp b/vibrator/1.1/Android.bp
index 0302220..357c1f0 100644
--- a/vibrator/1.1/Android.bp
+++ b/vibrator/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.vibrator@1.1",
     root: "android.hardware",
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.2/Android.bp b/vibrator/1.2/Android.bp
index 1fa0114..42f97d4 100644
--- a/vibrator/1.2/Android.bp
+++ b/vibrator/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.vibrator@1.2",
     root: "android.hardware",
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.3/Android.bp b/vibrator/1.3/Android.bp
index d742388..124f5d1 100644
--- a/vibrator/1.3/Android.bp
+++ b/vibrator/1.3/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.vibrator@1.3",
     root: "android.hardware",
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/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/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/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/vr/1.0/Android.bp b/vr/1.0/Android.bp
index 769ee3b..27d2ea8 100644
--- a/vr/1.0/Android.bp
+++ b/vr/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.vr@1.0",
     root: "android.hardware",
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/weaver/1.0/Android.bp b/weaver/1.0/Android.bp
index 11fc685..f7d452a 100644
--- a/weaver/1.0/Android.bp
+++ b/weaver/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.weaver@1.0",
     root: "android.hardware",
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/aidl/Android.bp b/weaver/aidl/Android.bp
index 5637e0a..60925ec 100644
--- a/weaver/aidl/Android.bp
+++ b/weaver/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.weaver",
     vendor_available: 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
index 29bd9a9..61627d9 100644
--- 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
@@ -12,7 +12,8 @@
  * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -37,6 +38,6 @@
   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 INCORRECT_KEY = 2;
-  const int THROTTLE = 3;
+  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
index 239cdac..7491f32 100644
--- 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
@@ -12,7 +12,8 @@
  * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -33,7 +34,7 @@
 package android.hardware.weaver;
 @VintfStability
 parcelable WeaverConfig {
-  long slots;
-  long keySize;
-  long valueSize;
+  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
index 7e5db59..47ee4c8 100644
--- 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
@@ -12,7 +12,8 @@
  * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/weaver/aidl/android/hardware/weaver/IWeaver.aidl b/weaver/aidl/android/hardware/weaver/IWeaver.aidl
index ebbfabe..f51034a 100644
--- a/weaver/aidl/android/hardware/weaver/IWeaver.aidl
+++ b/weaver/aidl/android/hardware/weaver/IWeaver.aidl
@@ -46,8 +46,8 @@
      * Read binder calls may return a ServiceSpecificException with the following error codes.
      */
     const int STATUS_FAILED = 1;
-    const int INCORRECT_KEY = 2;
-    const int THROTTLE = 3;
+    const int STATUS_INCORRECT_KEY = 2;
+    const int STATUS_THROTTLE = 3;
 
     /**
      * Attempts to retrieve the value stored in the identified slot.
diff --git a/weaver/aidl/android/hardware/weaver/WeaverConfig.aidl b/weaver/aidl/android/hardware/weaver/WeaverConfig.aidl
index 75d961e..a156a7b 100644
--- a/weaver/aidl/android/hardware/weaver/WeaverConfig.aidl
+++ b/weaver/aidl/android/hardware/weaver/WeaverConfig.aidl
@@ -21,14 +21,14 @@
     /**
      * The number of slots available.
      */
-    long slots;
+    int slots;
     /**
      * The number of bytes used for a key.
      */
-    long keySize;
+    int keySize;
     /**
      * The number of bytes used for a value.
      */
-    long valueSize;
+    int valueSize;
 }
 
diff --git a/weaver/aidl/default/Android.bp b/weaver/aidl/default/Android.bp
index d936828..37a9c94 100644
--- a/weaver/aidl/default/Android.bp
+++ b/weaver/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.weaver-service.example",
     relative_install_path: "hw",
@@ -25,7 +34,7 @@
         "Weaver.cpp",
     ],
     shared_libs: [
-        "android.hardware.weaver-ndk_platform",
+        "android.hardware.weaver-V1-ndk_platform",
         "libbase",
         "libbinder_ndk",
     ],
diff --git a/weaver/aidl/vts/Android.bp b/weaver/aidl/vts/Android.bp
index d7e3ab7..8dec4c1 100644
--- a/weaver/aidl/vts/Android.bp
+++ b/weaver/aidl/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 {
     name: "VtsHalWeaverTargetTest",
     defaults: [
@@ -25,7 +34,7 @@
         "libbinder_ndk",
         "libbase",
     ],
-    static_libs: ["android.hardware.weaver-ndk_platform"],
+    static_libs: ["android.hardware.weaver-V1-ndk_platform"],
     test_suites: [
         "general-tests",
         "vts",
diff --git a/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp b/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp
index 7d8daa2..878c762 100644
--- a/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp
+++ b/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp
@@ -156,7 +156,7 @@
 
     ASSERT_FALSE(readRet.isOk());
     ASSERT_EQ(EX_SERVICE_SPECIFIC, readRet.getExceptionCode());
-    ASSERT_EQ(IWeaver::INCORRECT_KEY, readRet.getServiceSpecificError());
+    ASSERT_EQ(IWeaver::STATUS_INCORRECT_KEY, readRet.getServiceSpecificError());
     EXPECT_TRUE(readValue.empty());
 }
 
diff --git a/wifi/1.0/Android.bp b/wifi/1.0/Android.bp
index c41864f..94f8462 100644
--- a/wifi/1.0/Android.bp
+++ b/wifi/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.wifi@1.0",
     root: "android.hardware",
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 4068b31..7b4116a 100644
--- a/wifi/1.1/Android.bp
+++ b/wifi/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.wifi@1.1",
     root: "android.hardware",
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 5812b82..f0edb81 100644
--- a/wifi/1.2/Android.bp
+++ b/wifi/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.wifi@1.2",
     root: "android.hardware",
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 f4e130a..030d7f6 100644
--- a/wifi/1.3/Android.bp
+++ b/wifi/1.3/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.wifi@1.3",
     root: "android.hardware",
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 5620d03..1523f79 100644
--- a/wifi/1.4/Android.bp
+++ b/wifi/1.4/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.wifi@1.4",
     root: "android.hardware",
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.h b/wifi/1.4/default/wifi_legacy_hal.h
index 1652ae9..822f83a 100644
--- a/wifi/1.4/default/wifi_legacy_hal.h
+++ b/wifi/1.4/default/wifi_legacy_hal.h
@@ -271,6 +271,7 @@
 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;
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/hostapd/1.0/Android.bp b/wifi/hostapd/1.0/Android.bp
index b736167..afcd45c 100644
--- a/wifi/hostapd/1.0/Android.bp
+++ b/wifi/hostapd/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.wifi.hostapd@1.0",
     root: "android.hardware",
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.1/Android.bp b/wifi/hostapd/1.1/Android.bp
index bba065d..f5f2fbe 100644
--- a/wifi/hostapd/1.1/Android.bp
+++ b/wifi/hostapd/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.wifi.hostapd@1.1",
     root: "android.hardware",
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.2/Android.bp b/wifi/hostapd/1.2/Android.bp
index 9b26ece..4ca41aa 100644
--- a/wifi/hostapd/1.2/Android.bp
+++ b/wifi/hostapd/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.wifi.hostapd@1.2",
     root: "android.hardware",
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/offload/1.0/Android.bp b/wifi/offload/1.0/Android.bp
index 91ed476..8fd602d 100644
--- a/wifi/offload/1.0/Android.bp
+++ b/wifi/offload/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.wifi.offload@1.0",
     root: "android.hardware",
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/supplicant/1.0/Android.bp b/wifi/supplicant/1.0/Android.bp
index d46e463..66e9353 100644
--- a/wifi/supplicant/1.0/Android.bp
+++ b/wifi/supplicant/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.wifi.supplicant@1.0",
     root: "android.hardware",
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/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp
index e4fe52c..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);
diff --git a/wifi/supplicant/1.1/Android.bp b/wifi/supplicant/1.1/Android.bp
index bc20dca..c624374 100644
--- a/wifi/supplicant/1.1/Android.bp
+++ b/wifi/supplicant/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.wifi.supplicant@1.1",
     root: "android.hardware",
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.2/Android.bp b/wifi/supplicant/1.2/Android.bp
index aa2fa7b..d5d937f 100644
--- a/wifi/supplicant/1.2/Android.bp
+++ b/wifi/supplicant/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.wifi.supplicant@1.2",
     root: "android.hardware",
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.3/Android.bp b/wifi/supplicant/1.3/Android.bp
index 4268490..fbe7f75 100644
--- a/wifi/supplicant/1.3/Android.bp
+++ b/wifi/supplicant/1.3/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.wifi.supplicant@1.3",
     root: "android.hardware",
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"],