Merge "Camera: Add android.scaler.rotateAndCrop control"
diff --git a/audio/6.0/config/api/current.txt b/audio/6.0/config/api/current.txt
index 0407d69..6b49e5e 100644
--- a/audio/6.0/config/api/current.txt
+++ b/audio/6.0/config/api/current.txt
@@ -214,6 +214,12 @@
     method public void set_default(boolean);
   }
 
+  public enum EngineSuffix {
+    method public String getRawName();
+    enum_constant public static final audio.policy.configuration.V6_0.EngineSuffix _default;
+    enum_constant public static final audio.policy.configuration.V6_0.EngineSuffix configurable;
+  }
+
   public enum GainMode {
     method public String getRawName();
     enum_constant public static final audio.policy.configuration.V6_0.GainMode AUDIO_GAIN_MODE_CHANNELS;
@@ -253,8 +259,10 @@
   public class GlobalConfiguration {
     ctor public GlobalConfiguration();
     method public boolean getCall_screen_mode_supported();
+    method public audio.policy.configuration.V6_0.EngineSuffix getEngine_library();
     method public boolean getSpeaker_drc_enabled();
     method public void setCall_screen_mode_supported(boolean);
+    method public void setEngine_library(audio.policy.configuration.V6_0.EngineSuffix);
     method public void setSpeaker_drc_enabled(boolean);
   }
 
diff --git a/audio/6.0/config/audio_policy_configuration.xsd b/audio/6.0/config/audio_policy_configuration.xsd
index 05c8ab4..341c6b3 100644
--- a/audio/6.0/config/audio_policy_configuration.xsd
+++ b/audio/6.0/config/audio_policy_configuration.xsd
@@ -67,6 +67,7 @@
     <xs:complexType name="globalConfiguration">
         <xs:attribute name="speaker_drc_enabled" type="xs:boolean" use="required"/>
         <xs:attribute name="call_screen_mode_supported" type="xs:boolean" use="optional"/>
+        <xs:attribute name="engine_library" type="engineSuffix" use="optional"/>
     </xs:complexType>
     <xs:complexType name="modules">
         <xs:annotation>
@@ -624,4 +625,10 @@
             </xs:element>
         </xs:sequence>
     </xs:complexType>
+    <xs:simpleType name="engineSuffix">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="default"/>
+            <xs:enumeration value="configurable"/>
+        </xs:restriction>
+    </xs:simpleType>
 </xs:schema>
diff --git a/audio/common/6.0/types.hal b/audio/common/6.0/types.hal
index 563e05d..26e8acb 100644
--- a/audio/common/6.0/types.hal
+++ b/audio/common/6.0/types.hal
@@ -762,6 +762,10 @@
     VIRTUAL_SOURCE                     = 15,
     ASSISTANT                          = 16,
     CALL_ASSISTANT                     = 17,
+    EMERGENCY                          = 1000,
+    SAFETY                             = 1001,
+    VEHICLE_STATUS                     = 1002,
+    ANNOUNCEMENT                       = 1003,
 };
 
 /** Type of audio generated by an application. */
diff --git a/audio/core/all-versions/default/PrimaryDevice.cpp b/audio/core/all-versions/default/PrimaryDevice.cpp
index 679f85d..11c1c5a 100644
--- a/audio/core/all-versions/default/PrimaryDevice.cpp
+++ b/audio/core/all-versions/default/PrimaryDevice.cpp
@@ -203,6 +203,9 @@
         case AudioMode::RINGTONE:
         case AudioMode::IN_CALL:
         case AudioMode::IN_COMMUNICATION:
+#if MAJOR_VERSION >= 6
+        case AudioMode::CALL_SCREEN:
+#endif
             break;  // Valid values
         default:
             return Result::INVALID_ARGUMENTS;
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 709b7cd..b0eb2e0 100644
--- a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
@@ -244,7 +244,13 @@
 TEST_P(AudioPrimaryHidlTest, setMode) {
     doc::test("Make sure setMode always succeeds if mode is valid and fails otherwise");
     // Test Invalid values
-    for (int mode : {-2, -1, int(AudioMode::IN_COMMUNICATION) + 1}) {
+#if MAJOR_VERSION >= 6
+    int maxMode = int(AudioMode::CALL_SCREEN);
+#else
+    int maxMode = int(AudioMode::IN_COMMUNICATION);
+#endif
+
+    for (int mode : {-2, -1, maxMode + 1}) {
         ASSERT_RESULT(Result::INVALID_ARGUMENTS, getDevice()->setMode(AudioMode(mode)))
                 << "mode=" << mode;
     }
@@ -253,6 +259,10 @@
                            AudioMode::NORMAL /* Make sure to leave the test in normal mode */}) {
         ASSERT_OK(getDevice()->setMode(mode)) << "mode=" << toString(mode);
     }
+    // AudioMode::CALL_SCREEN as support is optional
+#if MAJOR_VERSION >= 6
+    ASSERT_RESULT(okOrNotSupportedOrInvalidArgs, getDevice()->setMode(AudioMode::CALL_SCREEN));
+#endif
 }
 
 TEST_P(AudioPrimaryHidlTest, setBtHfpSampleRate) {
diff --git a/audio/policy/1.0/vts/OWNERS b/audio/policy/1.0/vts/OWNERS
new file mode 100644
index 0000000..24071af
--- /dev/null
+++ b/audio/policy/1.0/vts/OWNERS
@@ -0,0 +1,2 @@
+elaurent@google.com
+mnaganov@google.com
diff --git a/audio/policy/1.0/vts/functional/Android.bp b/audio/policy/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..b50e501
--- /dev/null
+++ b/audio/policy/1.0/vts/functional/Android.bp
@@ -0,0 +1,59 @@
+cc_test {
+    name: "VtsHalAudioPolicyV1_0TargetTest",
+    defaults: ["vts_target_tests_defaults"],
+    srcs: [
+        "ValidateEngineConfiguration.cpp",
+    ],
+    static_libs: [
+        "libxml2",
+        "liblog",
+        "libmedia_helper",
+        "libaudiopolicyengine_config",
+        "libaudiopolicycomponents",
+        "libaudiopolicyengineconfigurable_pfwwrapper",
+        "android.hardware.audio.common.test.utility",
+        "libparameter",
+        "libpfw_utility",
+        "libremote-processor",
+        "libutils",
+        "libcutils",
+        "libhidlbase",
+        "liblog",
+        "libbase",
+    ],
+    shared_libs: [
+        "libaudiofoundation",
+    ],
+    // Use test_config for vts-core suite.
+    // TODO(b/146104851): Add auto-gen rules and remove it.
+    test_config: "VtsHalAudioPolicyV1_0TargetTest.xml",
+    cflags: [
+        "-DXSD_DIR=\"/data/local/tmp\"",
+        "-DXSD_PFW_DIR=\"/data/local/tmp/Schemas\"",
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-function",
+        "-O0",
+        "-g",
+    ],
+    data: [
+        ":audio_policy_engine_conf_V1_0",
+        ":audio_policy_engine_configurable_configuration_V1_0",
+        ":audio_policy_engine_configurable_configuration_ComponentLibrary_V1_0",
+        ":audio_policy_engine_configurable_configuration_ComponentTypeSet_V1_0",
+        ":audio_policy_engine_configurable_configuration_ConfigurableDomain_V1_0",
+        ":audio_policy_engine_configurable_configuration_ConfigurableDomains_V1_0",
+        ":audio_policy_engine_configurable_configuration_FileIncluder_V1_0",
+        ":audio_policy_engine_configurable_configuration_Parameter_V1_0",
+        ":audio_policy_engine_configurable_configuration_ParameterFrameworkConfiguration_V1_0",
+        ":audio_policy_engine_configurable_configuration_ParameterSettings_V1_0",
+        ":audio_policy_engine_configurable_configuration_Subsystem_V1_0",
+        ":audio_policy_engine_configurable_configuration_SystemClass_V1_0",
+        ":audio_policy_engine_configurable_configuration_W3cXmlAttributes_V1_0",
+    ],
+    gtest: true,
+    test_suites: [
+        "general-tests",
+        "vts-core",
+    ],
+}
diff --git a/audio/policy/1.0/vts/functional/ValidateEngineConfiguration.cpp b/audio/policy/1.0/vts/functional/ValidateEngineConfiguration.cpp
new file mode 100644
index 0000000..a0aaa6e
--- /dev/null
+++ b/audio/policy/1.0/vts/functional/ValidateEngineConfiguration.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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 <EngineConfig.h>
+#include <ParameterManagerWrapper.h>
+
+#include <gtest/gtest.h>
+
+#include <unistd.h>
+#include <string>
+#include "utility/ValidateXml.h"
+
+static const std::vector<const char*> locations = {"/odm/etc", "/vendor/etc", "/system/etc"};
+static const std::string config = "audio_policy_engine_configuration.xml";
+static const std::string schema =
+        std::string(XSD_DIR) + "/audio_policy_engine_configuration_V1_0.xsd";
+
+static const std::string configurableSchemas =
+        std::string(XSD_DIR) + "/audio_policy_engine_configurable_configuration_V1_0.xsd";
+static const std::string configurableConfig =
+        "parameter-framework/ParameterFrameworkConfigurationPolicy.xml";
+
+/**
+ * @brief TEST to ensure the audio policy engine configuration file is validating schemas.
+ * Note: this configuration file is not mandatory, an hardcoded fallback is provided, so
+ * it does not fail if not found.
+ */
+TEST(ValidateConfiguration, audioPolicyEngineConfiguration) {
+    RecordProperty("description",
+                   "Verify that the audio policy engine configuration file "
+                   "is valid according to the schemas");
+    EXPECT_VALID_XML_MULTIPLE_LOCATIONS(config.c_str(), locations, schema.c_str());
+}
+
+/**
+ * @brief deviceUsesConfigurableEngine checks if the configuration file for
+ * the engine presents on the device AND
+ * for the configurable engine (aka Parameter-Framework top configuration file) presents.
+ */
+static bool deviceUsesConfigurableEngine() {
+    return android::hardware::audio::common::test::utility::validateXmlMultipleLocations<true>(
+                   "", "", "", config.c_str(), locations, schema.c_str()) &&
+           android::hardware::audio::common::test::utility::validateXmlMultipleLocations<true>(
+                   "", "", "", configurableConfig.c_str(), locations, configurableSchemas.c_str());
+}
+
+TEST(ValidateConfiguration, audioPolicyEngineConfigurable) {
+    if (!deviceUsesConfigurableEngine()) {
+        GTEST_SKIP() << "Device using legacy engine without parameter-framework, n-op.";
+    }
+    RecordProperty("description",
+                   "Verify that the audio policy engine PFW configuration files "
+                   "are valid according to the schemas");
+
+    auto testAudioPolicyEnginePfw = [&](bool validateSchema, const std::string& schemasUri) {
+        auto result = android::engineConfig::parse();
+
+        ASSERT_NE(nullptr, result.parsedConfig)
+                << "failed to parse audio policy engine configuration";
+
+        ASSERT_EQ(result.nbSkippedElement, 0) << "skipped %zu elements " << result.nbSkippedElement;
+
+        std::unique_ptr<android::audio_policy::ParameterManagerWrapper> policyParameterMgr(
+                new android::audio_policy::ParameterManagerWrapper(validateSchema, schemasUri));
+        ASSERT_NE(nullptr, policyParameterMgr) << "failed to create Audio Policy Engine PFW";
+
+        // Load the criterion types and criteria
+        for (auto& criterion : result.parsedConfig->criteria) {
+            android::engineConfig::CriterionType criterionType;
+            for (auto& configCriterionType : result.parsedConfig->criterionTypes) {
+                if (configCriterionType.name == criterion.typeName) {
+                    criterionType = configCriterionType;
+                    break;
+                }
+            }
+            ASSERT_FALSE(criterionType.name.empty())
+                    << "Invalid criterion type for " << criterion.name.c_str();
+            policyParameterMgr->addCriterion(criterion.name, criterionType.isInclusive,
+                                             criterionType.valuePairs,
+                                             criterion.defaultLiteralValue);
+        }
+        ASSERT_EQ(0, result.nbSkippedElement) << "failed to parse Audio Policy Engine PFW criteria";
+
+        // If the PFW cannot validate, it will not start
+        std::string error;
+        auto status = policyParameterMgr->start(error);
+        ASSERT_EQ(status, android::NO_ERROR)
+                << "failed to " << (validateSchema ? "validate" : "start")
+                << " Audio Policy Engine PFW: " << error;
+
+        ASSERT_TRUE(policyParameterMgr->isStarted());
+    };
+
+    // First round for sanity to ensure we can launch the Audio Policy Engine PFW without
+    // schema validation successfully, otherwise it is not forth going on running validation...
+    testAudioPolicyEnginePfw(false, {});
+
+    // If second round fails, it means parameter-framework cannot validate schema
+    testAudioPolicyEnginePfw(true, {XSD_PFW_DIR});
+}
diff --git a/audio/policy/1.0/vts/functional/VtsHalAudioPolicyV1_0TargetTest.xml b/audio/policy/1.0/vts/functional/VtsHalAudioPolicyV1_0TargetTest.xml
new file mode 100644
index 0000000..68b390f
--- /dev/null
+++ b/audio/policy/1.0/vts/functional/VtsHalAudioPolicyV1_0TargetTest.xml
@@ -0,0 +1,48 @@
+<?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 VtsHalAudioPolicyV1_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.RunCommandTargetPreparer">
+  </target_preparer>
+
+  <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+    <option name="cleanup" value="true" />
+    <option name="push" value="VtsHalAudioPolicyV1_0TargetTest->/data/local/tmp/VtsHalAudioPolicyV1_0TargetTest" />
+    <option name="push" value="audio_policy_engine_conf_V1_0.xsd->/data/local/tmp/audio_policy_engine_configuration_V1_0.xsd" />
+    <option name="push" value="audio_policy_engine_configurable_configuration_V1_0.xsd->/data/local/tmp/audio_policy_engine_configurable_configuration_V1_0.xsd" />
+    <option name="push" value="audio_policy_engine_configurable_configuration_ComponentLibrary_V1_0.xsd->/data/local/tmp/Schemas/ComponentLibrary.xsd" />
+    <option name="push" value="audio_policy_engine_configurable_configuration_ComponentTypeSet_V1_0.xsd->/data/local/tmp/Schemas/ComponentTypeSet.xsd" />
+    <option name="push" value="audio_policy_engine_configurable_configuration_ConfigurableDomain_V1_0.xsd->/data/local/tmp/Schemas/ConfigurableDomain.xsd" />
+    <option name="push" value="audio_policy_engine_configurable_configuration_ConfigurableDomains_V1_0.xsd->/data/local/tmp/Schemas/ConfigurableDomains.xsd" />
+    <option name="push" value="audio_policy_engine_configurable_configuration_FileIncluder_V1_0.xsd->/data/local/tmp/Schemas/FileIncluder.xsd" />
+    <option name="push" value="audio_policy_engine_configurable_configuration_Parameter_V1_0.xsd->/data/local/tmp/Schemas/Parameter.xsd" />
+    <option name="push" value="audio_policy_engine_configurable_configuration_ParameterFrameworkConfiguration_V1_0.xsd->/data/local/tmp/Schemas/ParameterFrameworkConfiguration.xsd" />
+    <option name="push" value="audio_policy_engine_configurable_configuration_ParameterSettings_V1_0.xsd->/data/local/tmp/Schemas/ParameterSettings.xsd" />
+    <option name="push" value="audio_policy_engine_configurable_configuration_Subsystem_V1_0.xsd->/data/local/tmp/Schemas/Subsystem.xsd" />
+    <option name="push" value="audio_policy_engine_configurable_configuration_SystemClass_V1_0.xsd->/data/local/tmp/Schemas/SystemClass.xsd" />
+    <option name="push" value="audio_policy_engine_configurable_configuration_W3cXmlAttributes_V1_0.xsd->/data/local/tmp/Schemas/W3cXmlAttributes.xsd" />
+  </target_preparer>
+
+  <test class="com.android.tradefed.testtype.GTest" >
+    <option name="native-test-device-path" value="/data/local/tmp" />
+    <option name="module-name" value="VtsHalAudioPolicyV1_0TargetTest" />
+  </test>
+</configuration>
diff --git a/audio/policy/1.0/xml/Android.bp b/audio/policy/1.0/xml/Android.bp
new file mode 100644
index 0000000..6da7b5a
--- /dev/null
+++ b/audio/policy/1.0/xml/Android.bp
@@ -0,0 +1,5 @@
+xsd_config {
+    name: "audio_policy_engine_conf_V1_0",
+    srcs: ["audio_policy_engine_configuration.xsd"],
+    package_name: "audio.policy.V1_0",
+}
diff --git a/audio/policy/1.0/xml/api/current.txt b/audio/policy/1.0/xml/api/current.txt
new file mode 100644
index 0000000..29a9cd4
--- /dev/null
+++ b/audio/policy/1.0/xml/api/current.txt
@@ -0,0 +1,296 @@
+// Signature format: 2.0
+package audio.policy.V1_0 {
+
+  public class AttributesGroup {
+    ctor public AttributesGroup();
+    method public java.util.List<audio.policy.V1_0.AttributesType> getAttributes_optional();
+    method public audio.policy.V1_0.BundleType getBundle_optional();
+    method public audio.policy.V1_0.ContentTypeType getContentType_optional();
+    method public audio.policy.V1_0.FlagsType getFlags_optional();
+    method public audio.policy.V1_0.SourceType getSource_optional();
+    method public audio.policy.V1_0.Stream getStreamType();
+    method public audio.policy.V1_0.UsageType getUsage_optional();
+    method public String getVolumeGroup();
+    method public void setBundle_optional(audio.policy.V1_0.BundleType);
+    method public void setContentType_optional(audio.policy.V1_0.ContentTypeType);
+    method public void setFlags_optional(audio.policy.V1_0.FlagsType);
+    method public void setSource_optional(audio.policy.V1_0.SourceType);
+    method public void setStreamType(audio.policy.V1_0.Stream);
+    method public void setUsage_optional(audio.policy.V1_0.UsageType);
+    method public void setVolumeGroup(String);
+  }
+
+  public class AttributesRef {
+    ctor public AttributesRef();
+    method public java.util.List<audio.policy.V1_0.AttributesRefType> getReference();
+  }
+
+  public class AttributesRefType {
+    ctor public AttributesRefType();
+    method public audio.policy.V1_0.AttributesType getAttributes();
+    method public String getName();
+    method public void setAttributes(audio.policy.V1_0.AttributesType);
+    method public void setName(String);
+  }
+
+  public class AttributesType {
+    ctor public AttributesType();
+    method public String getAttributesRef();
+    method public audio.policy.V1_0.BundleType getBundle();
+    method public audio.policy.V1_0.ContentTypeType getContentType();
+    method public audio.policy.V1_0.FlagsType getFlags();
+    method public audio.policy.V1_0.SourceType getSource();
+    method public audio.policy.V1_0.UsageType getUsage();
+    method public void setAttributesRef(String);
+    method public void setBundle(audio.policy.V1_0.BundleType);
+    method public void setContentType(audio.policy.V1_0.ContentTypeType);
+    method public void setFlags(audio.policy.V1_0.FlagsType);
+    method public void setSource(audio.policy.V1_0.SourceType);
+    method public void setUsage(audio.policy.V1_0.UsageType);
+  }
+
+  public class BundleType {
+    ctor public BundleType();
+    method public String getKey();
+    method public String getValue();
+    method public void setKey(String);
+    method public void setValue(String);
+  }
+
+  public class Configuration {
+    ctor public Configuration();
+    method public java.util.List<audio.policy.V1_0.AttributesRef> getAttributesRef();
+    method public java.util.List<audio.policy.V1_0.CriteriaType> getCriteria();
+    method public java.util.List<audio.policy.V1_0.CriterionTypesType> getCriterion_types();
+    method public java.util.List<audio.policy.V1_0.ProductStrategies> getProductStrategies();
+    method public audio.policy.V1_0.Version getVersion();
+    method public java.util.List<audio.policy.V1_0.VolumeGroupsType> getVolumeGroups();
+    method public java.util.List<audio.policy.V1_0.VolumesType> getVolumes();
+    method public void setVersion(audio.policy.V1_0.Version);
+  }
+
+  public enum ContentType {
+    method public String getRawName();
+    enum_constant public static final audio.policy.V1_0.ContentType AUDIO_CONTENT_TYPE_MOVIE;
+    enum_constant public static final audio.policy.V1_0.ContentType AUDIO_CONTENT_TYPE_MUSIC;
+    enum_constant public static final audio.policy.V1_0.ContentType AUDIO_CONTENT_TYPE_SONIFICATION;
+    enum_constant public static final audio.policy.V1_0.ContentType AUDIO_CONTENT_TYPE_SPEECH;
+    enum_constant public static final audio.policy.V1_0.ContentType AUDIO_CONTENT_TYPE_UNKNOWN;
+  }
+
+  public class ContentTypeType {
+    ctor public ContentTypeType();
+    method public audio.policy.V1_0.ContentType getValue();
+    method public void setValue(audio.policy.V1_0.ContentType);
+  }
+
+  public class CriteriaType {
+    ctor public CriteriaType();
+    method public java.util.List<audio.policy.V1_0.CriterionType> getCriterion();
+  }
+
+  public class CriterionType {
+    ctor public CriterionType();
+    method public String getName();
+    method public String getType();
+    method public String get_default();
+    method public void setName(String);
+    method public void setType(String);
+    method public void set_default(String);
+  }
+
+  public class CriterionTypeType {
+    ctor public CriterionTypeType();
+    method public String getName();
+    method public audio.policy.V1_0.PfwCriterionTypeEnum getType();
+    method public audio.policy.V1_0.ValuesType getValues();
+    method public void setName(String);
+    method public void setType(audio.policy.V1_0.PfwCriterionTypeEnum);
+    method public void setValues(audio.policy.V1_0.ValuesType);
+  }
+
+  public class CriterionTypesType {
+    ctor public CriterionTypesType();
+    method public java.util.List<audio.policy.V1_0.CriterionTypeType> getCriterion_type();
+  }
+
+  public enum DeviceCategory {
+    method public String getRawName();
+    enum_constant public static final audio.policy.V1_0.DeviceCategory DEVICE_CATEGORY_EARPIECE;
+    enum_constant public static final audio.policy.V1_0.DeviceCategory DEVICE_CATEGORY_EXT_MEDIA;
+    enum_constant public static final audio.policy.V1_0.DeviceCategory DEVICE_CATEGORY_HEADSET;
+    enum_constant public static final audio.policy.V1_0.DeviceCategory DEVICE_CATEGORY_HEARING_AID;
+    enum_constant public static final audio.policy.V1_0.DeviceCategory DEVICE_CATEGORY_SPEAKER;
+  }
+
+  public enum FlagType {
+    method public String getRawName();
+    enum_constant public static final audio.policy.V1_0.FlagType AUDIO_FLAG_AUDIBILITY_ENFORCED;
+    enum_constant public static final audio.policy.V1_0.FlagType AUDIO_FLAG_BEACON;
+    enum_constant public static final audio.policy.V1_0.FlagType AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY;
+    enum_constant public static final audio.policy.V1_0.FlagType AUDIO_FLAG_BYPASS_MUTE;
+    enum_constant public static final audio.policy.V1_0.FlagType AUDIO_FLAG_CAPTURE_PRIVATE;
+    enum_constant public static final audio.policy.V1_0.FlagType AUDIO_FLAG_DEEP_BUFFER;
+    enum_constant public static final audio.policy.V1_0.FlagType AUDIO_FLAG_HW_AV_SYNC;
+    enum_constant public static final audio.policy.V1_0.FlagType AUDIO_FLAG_HW_HOTWORD;
+    enum_constant public static final audio.policy.V1_0.FlagType AUDIO_FLAG_LOW_LATENCY;
+    enum_constant public static final audio.policy.V1_0.FlagType AUDIO_FLAG_MUTE_HAPTIC;
+    enum_constant public static final audio.policy.V1_0.FlagType AUDIO_FLAG_NONE;
+    enum_constant public static final audio.policy.V1_0.FlagType AUDIO_FLAG_NO_MEDIA_PROJECTION;
+    enum_constant public static final audio.policy.V1_0.FlagType AUDIO_FLAG_NO_SYSTEM_CAPTURE;
+    enum_constant public static final audio.policy.V1_0.FlagType AUDIO_FLAG_SCO;
+    enum_constant public static final audio.policy.V1_0.FlagType AUDIO_FLAG_SECURE;
+  }
+
+  public class FlagsType {
+    ctor public FlagsType();
+    method public java.util.List<audio.policy.V1_0.FlagType> getValue();
+    method public void setValue(java.util.List<audio.policy.V1_0.FlagType>);
+  }
+
+  public enum PfwCriterionTypeEnum {
+    method public String getRawName();
+    enum_constant public static final audio.policy.V1_0.PfwCriterionTypeEnum exclusive;
+    enum_constant public static final audio.policy.V1_0.PfwCriterionTypeEnum inclusive;
+  }
+
+  public class ProductStrategies {
+    ctor public ProductStrategies();
+    method public java.util.List<audio.policy.V1_0.ProductStrategies.ProductStrategy> getProductStrategy();
+  }
+
+  public static class ProductStrategies.ProductStrategy {
+    ctor public ProductStrategies.ProductStrategy();
+    method public java.util.List<audio.policy.V1_0.AttributesGroup> getAttributesGroup();
+    method public String getName();
+    method public void setName(String);
+  }
+
+  public enum SourceEnumType {
+    method public String getRawName();
+    enum_constant public static final audio.policy.V1_0.SourceEnumType AUDIO_SOURCE_CAMCORDER;
+    enum_constant public static final audio.policy.V1_0.SourceEnumType AUDIO_SOURCE_DEFAULT;
+    enum_constant public static final audio.policy.V1_0.SourceEnumType AUDIO_SOURCE_ECHO_REFERENCE;
+    enum_constant public static final audio.policy.V1_0.SourceEnumType AUDIO_SOURCE_FM_TUNER;
+    enum_constant public static final audio.policy.V1_0.SourceEnumType AUDIO_SOURCE_MIC;
+    enum_constant public static final audio.policy.V1_0.SourceEnumType AUDIO_SOURCE_REMOTE_SUBMIX;
+    enum_constant public static final audio.policy.V1_0.SourceEnumType AUDIO_SOURCE_UNPROCESSED;
+    enum_constant public static final audio.policy.V1_0.SourceEnumType AUDIO_SOURCE_VOICE_CALL;
+    enum_constant public static final audio.policy.V1_0.SourceEnumType AUDIO_SOURCE_VOICE_COMMUNICATION;
+    enum_constant public static final audio.policy.V1_0.SourceEnumType AUDIO_SOURCE_VOICE_DOWNLINK;
+    enum_constant public static final audio.policy.V1_0.SourceEnumType AUDIO_SOURCE_VOICE_PERFORMANCE;
+    enum_constant public static final audio.policy.V1_0.SourceEnumType AUDIO_SOURCE_VOICE_RECOGNITION;
+    enum_constant public static final audio.policy.V1_0.SourceEnumType AUDIO_SOURCE_VOICE_UPLINK;
+  }
+
+  public class SourceType {
+    ctor public SourceType();
+    method public audio.policy.V1_0.SourceEnumType getValue();
+    method public void setValue(audio.policy.V1_0.SourceEnumType);
+  }
+
+  public enum Stream {
+    method public String getRawName();
+    enum_constant public static final audio.policy.V1_0.Stream AUDIO_STREAM_ACCESSIBILITY;
+    enum_constant public static final audio.policy.V1_0.Stream AUDIO_STREAM_ALARM;
+    enum_constant public static final audio.policy.V1_0.Stream AUDIO_STREAM_ASSISTANT;
+    enum_constant public static final audio.policy.V1_0.Stream AUDIO_STREAM_BLUETOOTH_SCO;
+    enum_constant public static final audio.policy.V1_0.Stream AUDIO_STREAM_DEFAULT;
+    enum_constant public static final audio.policy.V1_0.Stream AUDIO_STREAM_DTMF;
+    enum_constant public static final audio.policy.V1_0.Stream AUDIO_STREAM_ENFORCED_AUDIBLE;
+    enum_constant public static final audio.policy.V1_0.Stream AUDIO_STREAM_MUSIC;
+    enum_constant public static final audio.policy.V1_0.Stream AUDIO_STREAM_NOTIFICATION;
+    enum_constant public static final audio.policy.V1_0.Stream AUDIO_STREAM_RING;
+    enum_constant public static final audio.policy.V1_0.Stream AUDIO_STREAM_SYSTEM;
+    enum_constant public static final audio.policy.V1_0.Stream AUDIO_STREAM_TTS;
+    enum_constant public static final audio.policy.V1_0.Stream AUDIO_STREAM_VOICE_CALL;
+  }
+
+  public enum UsageEnumType {
+    method public String getRawName();
+    enum_constant public static final audio.policy.V1_0.UsageEnumType AUDIO_USAGE_ALARM;
+    enum_constant public static final audio.policy.V1_0.UsageEnumType AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
+    enum_constant public static final audio.policy.V1_0.UsageEnumType AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
+    enum_constant public static final audio.policy.V1_0.UsageEnumType AUDIO_USAGE_ASSISTANCE_SONIFICATION;
+    enum_constant public static final audio.policy.V1_0.UsageEnumType AUDIO_USAGE_ASSISTANT;
+    enum_constant public static final audio.policy.V1_0.UsageEnumType AUDIO_USAGE_CALL_ASSISTANT;
+    enum_constant public static final audio.policy.V1_0.UsageEnumType AUDIO_USAGE_GAME;
+    enum_constant public static final audio.policy.V1_0.UsageEnumType AUDIO_USAGE_MEDIA;
+    enum_constant public static final audio.policy.V1_0.UsageEnumType AUDIO_USAGE_NOTIFICATION;
+    enum_constant public static final audio.policy.V1_0.UsageEnumType AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE;
+    enum_constant public static final audio.policy.V1_0.UsageEnumType AUDIO_USAGE_UNKNOWN;
+    enum_constant public static final audio.policy.V1_0.UsageEnumType AUDIO_USAGE_VIRTUAL_SOURCE;
+    enum_constant public static final audio.policy.V1_0.UsageEnumType AUDIO_USAGE_VOICE_COMMUNICATION;
+    enum_constant public static final audio.policy.V1_0.UsageEnumType AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
+  }
+
+  public class UsageType {
+    ctor public UsageType();
+    method public audio.policy.V1_0.UsageEnumType getValue();
+    method public void setValue(audio.policy.V1_0.UsageEnumType);
+  }
+
+  public class ValueType {
+    ctor public ValueType();
+    method public String getLiteral();
+    method public int getNumerical();
+    method public void setLiteral(String);
+    method public void setNumerical(int);
+  }
+
+  public class ValuesType {
+    ctor public ValuesType();
+    method public java.util.List<audio.policy.V1_0.ValueType> getValue();
+  }
+
+  public enum Version {
+    method public String getRawName();
+    enum_constant public static final audio.policy.V1_0.Version _1_0;
+  }
+
+  public class Volume {
+    ctor public Volume();
+    method public audio.policy.V1_0.DeviceCategory getDeviceCategory();
+    method public java.util.List<java.lang.String> getPoint();
+    method public String getRef();
+    method public void setDeviceCategory(audio.policy.V1_0.DeviceCategory);
+    method public void setRef(String);
+  }
+
+  public class VolumeGroupsType {
+    ctor public VolumeGroupsType();
+    method public java.util.List<audio.policy.V1_0.VolumeGroupsType.VolumeGroup> getVolumeGroup();
+  }
+
+  public static class VolumeGroupsType.VolumeGroup {
+    ctor public VolumeGroupsType.VolumeGroup();
+    method public int getIndexMax();
+    method public int getIndexMin();
+    method public String getName();
+    method public java.util.List<audio.policy.V1_0.Volume> getVolume();
+    method public void setIndexMax(int);
+    method public void setIndexMin(int);
+    method public void setName(String);
+  }
+
+  public class VolumeRef {
+    ctor public VolumeRef();
+    method public String getName();
+    method public java.util.List<java.lang.String> getPoint();
+    method public void setName(String);
+  }
+
+  public class VolumesType {
+    ctor public VolumesType();
+    method public java.util.List<audio.policy.V1_0.VolumeRef> getReference();
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method public static audio.policy.V1_0.Configuration read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+}
+
diff --git a/audio/policy/1.0/xml/api/last_current.txt b/audio/policy/1.0/xml/api/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/audio/policy/1.0/xml/api/last_current.txt
diff --git a/audio/policy/1.0/xml/api/last_removed.txt b/audio/policy/1.0/xml/api/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/audio/policy/1.0/xml/api/last_removed.txt
diff --git a/audio/policy/1.0/xml/api/removed.txt b/audio/policy/1.0/xml/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/audio/policy/1.0/xml/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/audio/policy/1.0/xml/audio_policy_engine_configuration.xsd b/audio/policy/1.0/xml/audio_policy_engine_configuration.xsd
new file mode 100644
index 0000000..842e724
--- /dev/null
+++ b/audio/policy/1.0/xml/audio_policy_engine_configuration.xsd
@@ -0,0 +1,402 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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.
+     -->
+
+ <xs:schema version="2.0"
+            elementFormDefault="qualified"
+            attributeFormDefault="unqualified"
+            xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <!-- List the config versions supported by audio policy engine. -->
+    <xs:simpleType name="version">
+        <xs:restriction base="xs:decimal">
+            <xs:enumeration value="1.0"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:element name="configuration">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="ProductStrategies" type="ProductStrategies"  minOccurs="0" maxOccurs="unbounded"/>
+                <xs:element name="criterion_types" type="criterionTypesType"  minOccurs="0" maxOccurs="unbounded"/>
+                <xs:element name="criteria" type="criteriaType"  minOccurs="0" maxOccurs="unbounded"/>
+                <xs:element name="volumeGroups" type="volumeGroupsType"  minOccurs="0" maxOccurs="unbounded"/>
+                <xs:element name="volumes" type="volumesType" minOccurs="0" maxOccurs="unbounded"/>
+                <xs:element name="attributesRef" type="attributesRef"  minOccurs="0" maxOccurs="unbounded"/>
+            </xs:sequence>
+            <xs:attribute name="version" type="version" use="required"/>
+        </xs:complexType>
+
+        <xs:key name="volumeCurveNameKey">
+            <xs:selector xpath="volumes/reference"/>
+            <xs:field xpath="@name"/>
+        </xs:key>
+        <xs:keyref name="volumeCurveRef" refer="volumeCurveNameKey">
+            <xs:selector xpath="volumeGroups/volumeGroup"/>
+            <xs:field xpath="@ref"/>
+        </xs:keyref>
+
+        <xs:key name="attributesRefNameKey">
+            <xs:selector xpath="attributesRef/reference"/>
+            <xs:field xpath="@name"/>
+        </xs:key>
+        <xs:keyref name="volumeGroupAttributesRef" refer="attributesRefNameKey">
+            <xs:selector xpath="volumeGroups/volumeGroup/volume"/>
+            <xs:field xpath="@attributesRef"/>
+        </xs:keyref>
+        <xs:keyref name="ProductStrategyAttributesRef" refer="attributesRefNameKey">
+            <xs:selector xpath="ProductStrategies/ProductStrategy/Attributes"/>
+            <xs:field xpath="@attributesRef"/>
+        </xs:keyref>
+
+        <xs:unique name="productStrategyNameUniqueness">
+            <xs:selector xpath="ProductStrategies/ProductStrategy"/>
+            <xs:field xpath="@name"/>
+        </xs:unique>
+
+        <!-- ensure validity of volume group referred in product strategy-->
+        <xs:key name="volumeGroupKey">
+            <xs:selector xpath="volumeGroups/volumeGroup/name"/>
+            <xs:field xpath="."/>
+        </xs:key>
+        <xs:keyref name="volumeGroupRef" refer="volumeGroupKey">
+            <xs:selector xpath="ProductStrategies/ProductStrategy/AttributesGroup"/>
+            <xs:field xpath="@volumeGroup"/>
+        </xs:keyref>
+
+        <xs:unique name="volumeTargetUniqueness">
+            <xs:selector xpath="volumeGroups/volumeGroup"/>
+            <xs:field xpath="@name"/>
+            <xs:field xpath="@deviceCategory"/>
+        </xs:unique>
+
+        <!-- ensure validity of criterion type referred in criterion-->
+        <xs:key name="criterionTypeKey">
+            <xs:selector xpath="criterion_types/criterion_type"/>
+            <xs:field xpath="@name"/>
+        </xs:key>
+        <xs:keyref name="criterionTypeKeyRef" refer="criterionTypeKey">
+            <xs:selector xpath="criteria/criterion"/>
+            <xs:field xpath="@type"/>
+        </xs:keyref>
+
+    </xs:element>
+
+    <xs:complexType name="ProductStrategies">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+            </xs:documentation>
+        </xs:annotation>
+        <xs:sequence>
+            <xs:element name="ProductStrategy" maxOccurs="unbounded">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:element name="AttributesGroup" type="AttributesGroup" minOccurs="1" maxOccurs="unbounded"/>
+                    </xs:sequence>
+                    <xs:attribute name="name" type="xs:string" use="required"/>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="AttributesGroup">
+        <xs:sequence>
+            <xs:choice minOccurs="0">
+                <xs:element name="Attributes" type="AttributesType" minOccurs="1" maxOccurs="unbounded"/>
+                <xs:sequence>
+                    <xs:element name="ContentType" type="ContentTypeType" minOccurs="0" maxOccurs="1"/>
+                    <xs:element name="Usage" type="UsageType" minOccurs="1" maxOccurs="1"/>
+                    <xs:element name="Source" type="SourceType" minOccurs="0" maxOccurs="1"/>
+                    <xs:element name="Flags" type="FlagsType" minOccurs="0" maxOccurs="1"/>
+                    <xs:element name="Bundle" type="BundleType" minOccurs="0" maxOccurs="1"/>
+                </xs:sequence>
+            </xs:choice>
+        </xs:sequence>
+        <xs:attribute name="streamType" type="stream" use="optional"/>
+        <xs:attribute name="volumeGroup" type="xs:string" use="optional"/>
+    </xs:complexType>
+
+    <xs:complexType name="volumeGroupsType">
+        <xs:sequence>
+             <xs:element name="volumeGroup" minOccurs="0" maxOccurs="unbounded">
+                <xs:complexType>
+                    <xs:sequence>
+                         <xs:element name="name" type="xs:token"/>
+                         <xs:element name="indexMin" type="xs:int" minOccurs="0" maxOccurs="1"/>
+                         <xs:element name="indexMax" type="xs:int" minOccurs="0" maxOccurs="1"/>
+                         <xs:element name="volume" type="volume" minOccurs="1" maxOccurs="unbounded"/>
+                     </xs:sequence>
+                </xs:complexType>
+                <xs:unique name="volumeAttributesUniqueness">
+                    <xs:selector xpath="volume"/>
+                    <xs:field xpath="deviceCategory"/>
+                </xs:unique>
+             </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="volumesType">
+        <xs:sequence>
+            <xs:element name="reference" type="volumeRef" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="attributesRef">
+        <xs:sequence>
+            <xs:element name="reference" type="attributesRefType" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="criteriaType">
+        <xs:sequence>
+            <xs:element name="criterion" type="criterionType" maxOccurs="unbounded"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="criterionType">
+        <xs:attribute name="name" type="xs:string" use="required"/>
+        <xs:attribute name="type" type="xs:string" use="required"/>
+        <xs:attribute name="default" type="xs:string" use="optional"/>
+    </xs:complexType>
+
+    <xs:complexType name="criterionTypesType">
+        <xs:sequence>
+            <xs:element name="criterion_type" type="criterionTypeType" maxOccurs="unbounded"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="criterionTypeType">
+        <xs:sequence>
+            <xs:element name="values" type="valuesType" minOccurs="0" maxOccurs="1"/>
+        </xs:sequence>
+        <xs:attribute name="name" type="xs:token" use="required"/>
+        <xs:attribute name="type" type="pfwCriterionTypeEnum" use="required"/>
+    </xs:complexType>
+
+    <xs:complexType name="valuesType">
+        <xs:sequence>
+            <xs:element name="value" type="valueType" maxOccurs="unbounded"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="valueType">
+        <xs:attribute name="literal" type="xs:string" use="required"/>
+        <xs:attribute name="numerical" type="xs:int" use="required"/>
+    </xs:complexType>
+
+    <xs:complexType name="attributesRefType">
+        <xs:sequence>
+            <xs:element name="Attributes" type="AttributesType" minOccurs="1" maxOccurs="1"/>
+        </xs:sequence>
+        <xs:attribute name="name" type="xs:token" use="required"/>
+    </xs:complexType>
+
+    <xs:complexType name="AttributesType">
+        <xs:sequence>
+            <xs:element name="ContentType" type="ContentTypeType" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="Usage" type="UsageType" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="Source" type="SourceType" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="Flags" type="FlagsType" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="Bundle" type="BundleType" minOccurs="0" maxOccurs="1"/>
+        </xs:sequence>
+        <xs:attribute name="attributesRef" type="xs:token" use="optional"/>
+        <!-- with xsd 1.1, it is impossible to make choice on either attributes or element...-->
+    </xs:complexType>
+
+    <xs:complexType name="ContentTypeType">
+        <xs:attribute name="value" type="contentType" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="UsageType">
+        <xs:attribute name="value" type="usageEnumType" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="SourceType">
+        <xs:attribute name="value" type="sourceEnumType" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="FlagsType">
+        <xs:attribute name="value" type="flagsEnumType" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="BundleType">
+        <xs:attribute name="key" type="xs:string" use="required"/>
+        <xs:attribute name="value" type="xs:string" use="required"/>
+    </xs:complexType>
+
+    <xs:complexType name="volume">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Volume section defines a volume curve for a given use case and device category.
+                It contains a list of points of this curve expressing the attenuation in Millibels
+                for a given volume index from 0 to 100.
+                <volume deviceCategory="DEVICE_CATEGORY_SPEAKER">
+                    <point>0,-9600</point>
+                    <point>100,0</point>
+                </volume>
+
+                It may also reference a reference/@name to avoid duplicating curves.
+                <volume deviceCategory="DEVICE_CATEGORY_SPEAKER" ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
+                <reference name="DEFAULT_MEDIA_VOLUME_CURVE">
+                    <point>0,-9600</point>
+                    <point>100,0</point>
+                </reference>
+            </xs:documentation>
+        </xs:annotation>
+        <xs:sequence>
+            <xs:element name="point" type="volumePoint" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute name="deviceCategory" type="deviceCategory"/>
+        <xs:attribute name="ref" type="xs:token" use="optional"/>
+    </xs:complexType>
+
+    <xs:complexType name="volumeRef">
+        <xs:sequence>
+            <xs:element name="point" type="volumePoint" minOccurs="2" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute name="name" type="xs:token" use="required"/>
+    </xs:complexType>
+
+    <xs:simpleType name="volumePoint">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Comma separated pair of number.
+                The fist one is the framework level (between 0 and 100).
+                The second one is the volume to send to the HAL.
+                The framework will interpolate volumes not specified.
+                Their MUST be at least 2 points specified.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:pattern value="([0-9]{1,2}|100),-?[0-9]+"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+
+    <xs:simpleType name="streamsCsv">
+        <xs:list>
+            <xs:simpleType>
+                <xs:restriction base="stream">
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:list>
+    </xs:simpleType>
+
+    <!-- Enum values of audio_stream_type_t in audio-base.h
+         TODO: avoid manual sync. -->
+    <xs:simpleType name="stream">
+        <xs:restriction base="xs:NMTOKEN">
+            <!--xs:pattern value="\c+(,\c+)*"/-->
+            <xs:enumeration value="AUDIO_STREAM_DEFAULT"/>
+            <xs:enumeration value="AUDIO_STREAM_VOICE_CALL"/>
+            <xs:enumeration value="AUDIO_STREAM_SYSTEM"/>
+            <xs:enumeration value="AUDIO_STREAM_RING"/>
+            <xs:enumeration value="AUDIO_STREAM_MUSIC"/>
+            <xs:enumeration value="AUDIO_STREAM_ALARM"/>
+            <xs:enumeration value="AUDIO_STREAM_NOTIFICATION"/>
+            <xs:enumeration value="AUDIO_STREAM_BLUETOOTH_SCO"/>
+            <xs:enumeration value="AUDIO_STREAM_ENFORCED_AUDIBLE"/>
+            <xs:enumeration value="AUDIO_STREAM_DTMF"/>
+            <xs:enumeration value="AUDIO_STREAM_TTS"/>
+            <xs:enumeration value="AUDIO_STREAM_ACCESSIBILITY"/>
+            <xs:enumeration value="AUDIO_STREAM_ASSISTANT"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="deviceCategory">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="DEVICE_CATEGORY_HEADSET"/>
+            <xs:enumeration value="DEVICE_CATEGORY_SPEAKER"/>
+            <xs:enumeration value="DEVICE_CATEGORY_EARPIECE"/>
+            <xs:enumeration value="DEVICE_CATEGORY_EXT_MEDIA"/>
+            <xs:enumeration value="DEVICE_CATEGORY_HEARING_AID"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="contentType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_UNKNOWN"/>
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_SPEECH"/>
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_MUSIC"/>
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_MOVIE"/>
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_SONIFICATION"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="usageEnumType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_USAGE_UNKNOWN"/>
+            <xs:enumeration value="AUDIO_USAGE_MEDIA"/>
+            <xs:enumeration value="AUDIO_USAGE_VOICE_COMMUNICATION"/>
+            <xs:enumeration value="AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING"/>
+            <xs:enumeration value="AUDIO_USAGE_ALARM"/>
+            <xs:enumeration value="AUDIO_USAGE_NOTIFICATION"/>
+            <xs:enumeration value="AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE"/>
+            <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY"/>
+            <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"/>
+            <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_SONIFICATION"/>
+            <xs:enumeration value="AUDIO_USAGE_GAME"/>
+            <xs:enumeration value="AUDIO_USAGE_VIRTUAL_SOURCE"/>
+            <xs:enumeration value="AUDIO_USAGE_ASSISTANT"/>
+            <xs:enumeration value="AUDIO_USAGE_CALL_ASSISTANT"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="flagsEnumType">
+        <xs:list>
+            <xs:simpleType>
+                <xs:restriction base="flagType">
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:list>
+    </xs:simpleType>
+
+    <xs:simpleType name="flagType">
+        <xs:restriction base="xs:NMTOKEN">
+            <xs:enumeration value="AUDIO_FLAG_NONE"/>
+            <xs:enumeration value="AUDIO_FLAG_AUDIBILITY_ENFORCED"/>
+            <xs:enumeration value="AUDIO_FLAG_SECURE"/>
+            <xs:enumeration value="AUDIO_FLAG_SCO"/>
+            <xs:enumeration value="AUDIO_FLAG_BEACON"/>
+            <xs:enumeration value="AUDIO_FLAG_HW_AV_SYNC"/>
+            <xs:enumeration value="AUDIO_FLAG_HW_HOTWORD"/>
+            <xs:enumeration value="AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY"/>
+            <xs:enumeration value="AUDIO_FLAG_BYPASS_MUTE"/>
+            <xs:enumeration value="AUDIO_FLAG_LOW_LATENCY"/>
+            <xs:enumeration value="AUDIO_FLAG_DEEP_BUFFER"/>
+            <xs:enumeration value="AUDIO_FLAG_NO_MEDIA_PROJECTION"/>
+            <xs:enumeration value="AUDIO_FLAG_MUTE_HAPTIC"/>
+            <xs:enumeration value="AUDIO_FLAG_NO_SYSTEM_CAPTURE"/>
+            <xs:enumeration value="AUDIO_FLAG_CAPTURE_PRIVATE"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="sourceEnumType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_SOURCE_DEFAULT"/>
+            <xs:enumeration value="AUDIO_SOURCE_MIC"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_UPLINK"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_DOWNLINK"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_CALL"/>
+            <xs:enumeration value="AUDIO_SOURCE_CAMCORDER"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_RECOGNITION"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_COMMUNICATION"/>
+            <xs:enumeration value="AUDIO_SOURCE_REMOTE_SUBMIX"/>
+            <xs:enumeration value="AUDIO_SOURCE_UNPROCESSED"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_PERFORMANCE"/>
+            <xs:enumeration value="AUDIO_SOURCE_ECHO_REFERENCE"/>
+            <xs:enumeration value="AUDIO_SOURCE_FM_TUNER"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="pfwCriterionTypeEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="inclusive"/>
+            <xs:enumeration value="exclusive"/>
+        </xs:restriction>
+    </xs:simpleType>
+</xs:schema>
diff --git a/audio/policy/1.0/xml/pfw_schemas/AllSchemas.xsd b/audio/policy/1.0/xml/pfw_schemas/AllSchemas.xsd
new file mode 100644
index 0000000..1e04a38
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/AllSchemas.xsd
@@ -0,0 +1,754 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+ <!-- BEGIN W3cXmlAttributes.xsd -->
+ <xs:annotation>
+  <xs:documentation>
+   See http://www.w3.org/XML/1998/namespace.html and
+   http://www.w3.org/TR/REC-xml for information about this namespace.
+
+    This schema document describes the XML namespace, in a form
+    suitable for import by other schema documents.
+
+    Note that local names in this namespace are intended to be defined
+    only by the World Wide Web Consortium or its subgroups.  The
+    following names are currently defined in this namespace and should
+    not be used with conflicting semantics by any Working Group,
+    specification, or document instance:
+
+    base (as an attribute name): denotes an attribute whose value
+         provides a URI to be used as the base for interpreting any
+         relative URIs in the scope of the element on which it
+         appears; its value is inherited.  This name is reserved
+         by virtue of its definition in the XML Base specification.
+
+    id   (as an attribute name): denotes an attribute whose value
+         should be interpreted as if declared to be of type ID.
+         The xml:id specification is not yet a W3C Recommendation,
+         but this attribute is included here to facilitate experimentation
+         with the mechanisms it proposes.  Note that it is _not_ included
+         in the specialAttrs attribute group.
+
+    lang (as an attribute name): denotes an attribute whose value
+         is a language code for the natural language of the content of
+         any element; its value is inherited.  This name is reserved
+         by virtue of its definition in the XML specification.
+
+    space (as an attribute name): denotes an attribute whose
+         value is a keyword indicating what whitespace processing
+         discipline is intended for the content of the element; its
+         value is inherited.  This name is reserved by virtue of its
+         definition in the XML specification.
+
+    Father (in any context at all): denotes Jon Bosak, the chair of
+         the original XML Working Group.  This name is reserved by
+         the following decision of the W3C XML Plenary and
+         XML Coordination groups:
+
+             In appreciation for his vision, leadership and dedication
+             the W3C XML Plenary on this 10th day of February, 2000
+             reserves for Jon Bosak in perpetuity the XML name
+             xml:Father
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+  <xs:documentation>This schema defines attributes and an attribute group
+        suitable for use by
+        schemas wishing to allow xml:base, xml:lang, xml:space or xml:id
+        attributes on elements they define.
+
+        To enable this, such a schema must import this schema
+        for the XML namespace, e.g. as follows:
+        &lt;schema . . .>
+         . . .
+         &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+                    schemaLocation="http://www.w3.org/2005/08/xml.xsd"/>
+
+        Subsequently, qualified reference to any of the attributes
+        or the group defined below will have the desired effect, e.g.
+
+        &lt;type . . .>
+         . . .
+         &lt;attributeGroup ref="xml:specialAttrs"/>
+
+         will define a type which will schema-validate an instance
+         element with any of those attributes</xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+  <xs:documentation>In keeping with the XML Schema WG's standard versioning
+   policy, this schema document will persist at
+   http://www.w3.org/2005/08/xml.xsd.
+   At the date of issue it can also be found at
+   http://www.w3.org/2001/xml.xsd.
+   The schema document at that URI may however change in the future,
+   in order to remain compatible with the latest version of XML Schema
+   itself, or with the XML namespace itself.  In other words, if the XML
+   Schema or XML namespaces change, the version of this document at
+   http://www.w3.org/2001/xml.xsd will change
+   accordingly; the version at
+   http://www.w3.org/2005/08/xml.xsd will not change.
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:attribute name="lang">
+  <xs:annotation>
+   <xs:documentation>Attempting to install the relevant ISO 2- and 3-letter
+         codes as the enumerated possible values is probably never
+         going to be a realistic possibility.  See
+         RFC 3066 at http://www.ietf.org/rfc/rfc3066.txt and the IANA registry
+         at http://www.iana.org/assignments/lang-tag-apps.htm for
+         further information.
+
+         The union allows for the 'un-declaration' of xml:lang with
+         the empty string.</xs:documentation>
+  </xs:annotation>
+  <xs:simpleType>
+   <xs:union memberTypes="xs:language">
+    <xs:simpleType name="langEnum">
+     <xs:restriction base="xs:string">
+      <xs:enumeration value=""/>
+     </xs:restriction>
+    </xs:simpleType>
+   </xs:union>
+  </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="space">
+  <xs:simpleType name="spaceEnum">
+   <xs:restriction base="xs:NCName">
+    <xs:enumeration value="default"/>
+    <xs:enumeration value="preserve"/>
+   </xs:restriction>
+  </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="base" type="xs:anyURI">
+  <xs:annotation>
+   <xs:documentation>See http://www.w3.org/TR/xmlbase/ for
+                     information about this attribute.</xs:documentation>
+  </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="id" type="xs:ID">
+  <xs:annotation>
+   <xs:documentation>See http://www.w3.org/TR/xml-id/ for
+                     information about this attribute.</xs:documentation>
+  </xs:annotation>
+ </xs:attribute>
+
+ <xs:attributeGroup name="specialAttrs">
+  <xs:attribute ref="xml:base"/>
+  <xs:attribute ref="xml:lang"/>
+  <xs:attribute ref="xml:space"/>
+ </xs:attributeGroup>
+ <!-- END W3cXmlAttributes.xsd -->
+
+    <!-- BEGIN ParameterSettings.xsd -->
+<!-- BUG b/147297854 - removed "abstract" from type definition -->
+    <xs:complexType name="ParameterType">
+        <xs:simpleContent>
+            <xs:extension base="xs:string">
+                <xs:attribute name="Name" type="xs:NMTOKEN" use="required"/>
+                <xs:attribute name="ValueSpace" use="optional">
+                    <xs:simpleType name="ValueSpaceEnum">
+                        <xs:restriction base="xs:string">
+                            <xs:enumeration value="Raw"/>
+                            <xs:enumeration value="Real"/>
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:attribute>
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+    <xs:complexType name="BooleanParameterType">
+        <xs:simpleContent>
+            <xs:restriction base="ParameterType">
+                <xs:pattern value="([01][\s]*)+"/>
+                <xs:pattern value="((0x0|0x1)[\s]*)+"/>
+                <xs:attribute name="ValueSpace" use="prohibited"/>
+            </xs:restriction>
+        </xs:simpleContent>
+    </xs:complexType>
+    <xs:complexType name="IntegerParameterType">
+        <xs:simpleContent>
+            <xs:restriction base="ParameterType">
+                <xs:pattern value="(0|([+-]?[1-9][0-9]*))(\s+(0|([+-]?[1-9][0-9]*)))*"/>
+                <xs:pattern value="(0x[0-9a-fA-F]+)(\s+(0x[0-9a-fA-F]+))*"/>
+                <xs:attribute name="ValueSpace" use="prohibited"/>
+            </xs:restriction>
+        </xs:simpleContent>
+    </xs:complexType>
+    <xs:complexType name="EnumParameterType">
+        <xs:simpleContent>
+            <xs:restriction base="ParameterType">
+                <xs:attribute name="ValueSpace" use="prohibited"/>
+            </xs:restriction>
+        </xs:simpleContent>
+    </xs:complexType>
+    <xs:complexType name="PointParameterType">
+        <xs:simpleContent>
+            <xs:restriction base="ParameterType">
+                <xs:pattern value="((0|[+-]?0\.[0-9]+|(([+-]?[1-9][0-9]*)(\.[0-9]+)?))([Ee][+-]?[0-9]+)?)(\s+(0|[+-]?0\.[0-9]+|(([+-]?[1-9][0-9]*)(\.[0-9]+)?))([Ee][+-]?[0-9]+)?)*"/>
+                <xs:pattern value="(0x[0-9a-fA-F]+)(\s+(0x[0-9a-fA-F]+))*"/>
+            </xs:restriction>
+        </xs:simpleContent>
+    </xs:complexType>
+    <xs:complexType name="BitParameterBlockType">
+        <xs:sequence>
+            <xs:element name="BitParameter" maxOccurs="unbounded" type="IntegerParameterType"/>
+        </xs:sequence>
+        <xs:attribute name="Name" type="xs:NMTOKEN" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="StringParameterType">
+        <xs:simpleContent>
+            <xs:extension base="xs:string">
+                <xs:attribute name="Name" type="xs:NMTOKEN" use="required"/>
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+    <xs:group name="ParameterBlockGroup">
+        <xs:choice>
+            <xs:element name="BooleanParameter" type="BooleanParameterType"/>
+            <xs:element name="IntegerParameter" type="IntegerParameterType"/>
+            <xs:element name="EnumParameter" type="EnumParameterType"/>
+            <xs:element name="FixedPointParameter" type="PointParameterType"/>
+            <xs:element name="FloatingPointParameter" type="PointParameterType"/>
+            <xs:element name="BitParameterBlock" type="BitParameterBlockType">
+                <xs:unique name="BitParameterBlockSubElementsUniqueness">
+                    <xs:selector xpath="*"/>
+                    <xs:field xpath="@Name"/>
+                </xs:unique>
+            </xs:element>
+            <xs:element name="StringParameter" type="StringParameterType"/>
+            <xs:element name="Component" type="ParameterBlockType"/>
+            <xs:element name="ParameterBlock" type="ParameterBlockType">
+                <xs:unique name="ParameterBlockSubElementsUniqueness">
+                    <xs:selector xpath="*"/>
+                    <xs:field xpath="@Name"/>
+                </xs:unique>
+            </xs:element>
+        </xs:choice>
+    </xs:group>
+    <xs:complexType name="ParameterBlockType">
+        <xs:sequence>
+            <xs:group ref="ParameterBlockGroup" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute name="Name" type="xs:NMTOKEN" use="required"/>
+    </xs:complexType>
+    <!-- END ParameterSettings.xsd -->
+
+    <!-- BEGIN ConfigurableDomain.xsd -->
+    <xs:complexType name="SelectionCriterionRuleType">
+        <xs:attribute name="SelectionCriterion" type="xs:NMTOKEN" use="required"/>
+        <xs:attribute name="MatchesWhen" use="required">
+            <xs:simpleType name="MatchesWhenEnum">
+                <xs:restriction base="xs:NMTOKEN">
+                    <xs:enumeration value="Is"/>
+                    <xs:enumeration value="IsNot"/>
+                    <xs:enumeration value="Includes"/>
+                    <xs:enumeration value="Excludes"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+        <xs:attribute name="Value" use="required" type="xs:NMTOKEN"/>
+    </xs:complexType>
+    <xs:group name="RuleGroup">
+        <xs:choice>
+            <xs:element name="CompoundRule" type="CompoundRuleType"/>
+            <xs:element name="SelectionCriterionRule" type="SelectionCriterionRuleType"/>
+        </xs:choice>
+    </xs:group>
+    <xs:complexType name="CompoundRuleType">
+        <xs:sequence>
+            <xs:group ref="RuleGroup" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute name="Type">
+            <xs:simpleType name="TypeEnum">
+                <xs:restriction base="xs:NMTOKEN">
+                    <xs:enumeration value="Any"/>
+                    <xs:enumeration value="All"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+    </xs:complexType>
+    <xs:complexType name="ConfigurationsType">
+        <xs:sequence>
+            <xs:element maxOccurs="unbounded" name="Configuration">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:element name="CompoundRule" type="CompoundRuleType" minOccurs="0" maxOccurs="1"/>
+                    </xs:sequence>
+                    <xs:attribute name="Name" use="required" type="xs:NCName"/>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:group name="ComponentGroup">
+        <xs:sequence>
+            <xs:group ref="ParameterBlockGroup"/>
+        </xs:sequence>
+    </xs:group>
+    <xs:complexType name="ComponentType">
+        <xs:sequence>
+            <xs:choice>
+                <xs:group ref="ComponentGroup" maxOccurs="unbounded"/>
+                <xs:element name="Subsystem" type="ComponentType" maxOccurs="unbounded"/>
+            </xs:choice>
+        </xs:sequence>
+        <xs:attribute name="Name" use="required" type="xs:NCName"/>
+    </xs:complexType>
+    <xs:complexType name="ConfigurableElementsType">
+        <xs:sequence>
+            <xs:element maxOccurs="unbounded" minOccurs="0" name="ConfigurableElement">
+                <xs:complexType>
+                    <xs:attribute name="Path" use="required">
+                        <xs:simpleType>
+                            <xs:restriction base="xs:anyURI">
+                                <xs:pattern value="/.*[^/]"/>
+                            </xs:restriction>
+                        </xs:simpleType>
+                    </xs:attribute>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="ConfigurableElementSettingsType">
+        <xs:choice>
+            <xs:element name="BitParameter" type="IntegerParameterType"/>
+            <xs:group ref="ComponentGroup"/>
+        </xs:choice>
+        <xs:attribute name="Path" use="required">
+            <xs:simpleType>
+                <xs:restriction base="xs:anyURI">
+                    <xs:pattern value="/.*[^/]"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+    </xs:complexType>
+    <xs:complexType name="SettingsType">
+        <xs:sequence>
+            <xs:element maxOccurs="unbounded" minOccurs="0" name="Configuration">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:element name="ConfigurableElement" minOccurs="0" maxOccurs="unbounded" type="ConfigurableElementSettingsType"/>
+                    </xs:sequence>
+                    <xs:attribute name="Name" use="required" type="xs:NCName"/>
+                </xs:complexType>
+                <xs:unique name="ConfigurableElementUniqueness">
+                    <xs:selector xpath="ConfigurableElement"/>
+                    <xs:field xpath="@Path"/>
+                </xs:unique>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="ConfigurableDomainType">
+        <xs:sequence>
+            <xs:element name="Configurations" type="ConfigurationsType"/>
+            <xs:element name="ConfigurableElements" type="ConfigurableElementsType"/>
+            <xs:element name="Settings" type="SettingsType" minOccurs="0"/>
+        </xs:sequence>
+        <xs:attribute name="Name" use="required" type="xs:NCName"/>
+        <xs:attribute name="SequenceAware" use="optional" type="xs:boolean" default="false"/>
+    </xs:complexType>
+    <xs:element name="ConfigurableDomain" type="ConfigurableDomainType"/>
+    <!-- END ConfigurableDomain.xsd -->
+
+    <!-- BEGIN ConfigurableDomains.xsd -->
+    <xs:element name="ConfigurableDomains">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element maxOccurs="unbounded" name="ConfigurableDomain" type="ConfigurableDomainType">
+                    <xs:key name="ConfigurableElementKey">
+                        <xs:selector xpath="ConfigurableElements/ConfigurableElement"/>
+                        <xs:field xpath="@Path"/>
+                    </xs:key>
+                    <xs:keyref refer="ConfigurableElementKey" name="ConfigurableDomainReference">
+                        <xs:selector xpath="Settings/Configuration/ConfigurableElement"/>
+                        <xs:field xpath="@Path"/>
+                    </xs:keyref>
+                    <xs:key name="ConfigurationKey">
+                        <xs:selector xpath="Configurations/Configuration"/>
+                        <xs:field xpath="@Name"/>
+                    </xs:key>
+                    <xs:keyref refer="ConfigurationKey" name="ConfigurationReference2">
+                        <xs:selector xpath="ConfigurableElements/ConfigurableElement/Configuration"/>
+                        <xs:field xpath="@Name"/>
+                    </xs:keyref>
+                    <xs:keyref refer="ConfigurationKey" name="ConfigurationReference">
+                        <xs:selector xpath="Settings/Configuration"/>
+                        <xs:field xpath="@Name"/>
+                    </xs:keyref>
+                </xs:element>
+            </xs:sequence>
+            <xs:attribute name="SystemClassName" use="required" type="xs:NCName"/>
+        </xs:complexType>
+        <xs:unique name="ConfigurableDomainUniqueness">
+            <xs:selector xpath="ConfigurableDomain"/>
+            <xs:field xpath="@Name"/>
+        </xs:unique>
+    </xs:element>
+    <!-- END ConfigurableDomains.xsd -->
+
+    <!-- BEGIN Parameter.xsd -->
+    <xs:attributeGroup name="Nameable">
+        <xs:attribute name="Name" type="xs:NMTOKEN" use="required"/>
+        <xs:attribute name="Description" type="xs:string" use="optional"/>
+    </xs:attributeGroup>
+    <xs:attributeGroup name="TypedNameable">
+        <xs:attributeGroup ref="Nameable"/>
+        <xs:attribute name="Type" type="xs:NMTOKEN" use="required"/>
+    </xs:attributeGroup>
+    <xs:complexType name="ComponentInstance">
+        <xs:attributeGroup ref="TypedNameable"/>
+        <xs:attributeGroup ref="ArrayLengthAttribute"/>
+        <xs:attribute name="Mapping" use="optional" type="xs:string"/>
+    </xs:complexType>
+    <xs:simpleType name="SizeType">
+        <xs:restriction base="xs:positiveInteger">
+            <xs:pattern value="8|16|32"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="SizeType64">
+        <xs:restriction base="xs:positiveInteger">
+            <xs:pattern value="8|16|32|64"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:attributeGroup name="IntegerParameterAttributes">
+        <xs:attribute name="Size" type="SizeType" use="required"/>
+        <xs:attribute name="Min" type="xs:integer" use="optional"/>
+        <xs:attribute name="Max" type="xs:integer" use="optional"/>
+        <xs:attribute name="Signed" type="xs:boolean" use="optional" default="false"/>
+    </xs:attributeGroup>
+    <xs:attributeGroup name="ArrayLengthAttribute">
+        <xs:attribute name="ArrayLength" type="xs:nonNegativeInteger" use="optional" default="0"/>
+    </xs:attributeGroup>
+    <xs:complexType name="Adaptation">
+        <xs:attribute name="Offset" type="xs:integer" default="0"/>
+    </xs:complexType>
+    <xs:complexType name="LinearAdaptationType">
+        <xs:complexContent>
+            <xs:extension base="Adaptation">
+                <xs:attribute name="SlopeNumerator" type="xs:double" default="1"/>
+                <xs:attribute name="SlopeDenominator" type="xs:double" default="1"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="LinearAdaptation" type="LinearAdaptationType"/>
+    <xs:element name="LogarithmicAdaptation">
+        <xs:complexType>
+            <xs:complexContent>
+                <xs:extension base="LinearAdaptationType">
+                    <xs:attribute name="LogarithmBase" type="xs:double" default="10"/>
+                    <xs:attribute name="FloorValue" type="xs:double" default="-INF"/>
+                </xs:extension>
+            </xs:complexContent>
+        </xs:complexType>
+    </xs:element>
+<!-- BUG b/147297854 - removed abstract from Parameter definition -->
+    <xs:complexType name="Parameter">
+        <xs:attributeGroup ref="Nameable"/>
+        <xs:attribute name="Mapping" type="xs:string" use="optional"/>
+        <xs:attributeGroup ref="ArrayLengthAttribute"/>
+    </xs:complexType>
+    <xs:element name="BooleanParameter">
+        <xs:complexType>
+            <xs:complexContent>
+                <xs:extension base="Parameter">
+                    <xs:attribute name="Size" fixed="8" type="SizeType"/>
+                </xs:extension>
+            </xs:complexContent>
+        </xs:complexType>
+    </xs:element>
+    <xs:complexType name="IntegerParameterType">
+        <xs:complexContent>
+            <xs:extension base="Parameter">
+                <xs:choice minOccurs="0">
+                    <xs:element ref="LinearAdaptation"/>
+                    <xs:element ref="LogarithmicAdaptation"/>
+                </xs:choice>
+                <xs:attributeGroup ref="IntegerParameterAttributes"/>
+                <xs:attribute name="Unit" type="xs:token" use="optional"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="IntegerParameter" type="IntegerParameterType"/>
+    <xs:complexType name="EnumParameterType">
+        <xs:complexContent>
+            <xs:extension base="Parameter">
+                <xs:sequence>
+                    <xs:element name="ValuePair" maxOccurs="unbounded">
+                        <xs:complexType>
+                            <xs:attribute name="Literal" type="xs:string" use="required"/>
+                            <xs:attribute name="Numerical" use="required">
+                                <xs:simpleType>
+                                    <xs:restriction base="xs:string">
+                                        <xs:pattern value="0|[+-]?[1-9][0-9]*"/>
+                                        <xs:pattern value="0x[0-9a-fA-F]+"/>
+                                    </xs:restriction>
+                                </xs:simpleType>
+                            </xs:attribute>
+                        </xs:complexType>
+                    </xs:element>
+                </xs:sequence>
+                <xs:attribute name="Size" type="SizeType" use="required"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="EnumParameter" type="EnumParameterType">
+        <xs:unique name="LiteralUniqueness">
+            <xs:selector xpath="ValuePair"/>
+            <xs:field xpath="@Literal"/>
+        </xs:unique>
+        <xs:unique name="NumericalUniqueness">
+            <xs:selector xpath="ValuePair"/>
+            <xs:field xpath="@Numerical"/>
+        </xs:unique>
+    </xs:element>
+    <xs:simpleType name="PointBound">
+        <xs:restriction base="xs:string">
+            <xs:pattern value="(0|[+-]?0\.[0-9]+|(([+-]?[1-9][0-9]*)(\.[0-9]+)?))([Ee][+-]?[0-9]+)?"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="PointParameterType">
+        <xs:complexContent>
+            <xs:extension base="Parameter">
+                <xs:attribute name="Unit" type="xs:token" use="optional"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:complexType name="FixedPointParameterType">
+        <xs:complexContent>
+            <xs:extension base="PointParameterType">
+                <xs:attribute name="Size" type="SizeType" use="required"/>
+                <xs:attribute name="Integral" type="xs:nonNegativeInteger" use="required"/>
+                <xs:attribute name="Fractional" type="xs:nonNegativeInteger" use="required"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="FixedPointParameter" type="FixedPointParameterType"/>
+    <xs:complexType name="FloatingPointParameterType">
+        <xs:complexContent>
+            <xs:extension base="PointParameterType">
+                <xs:attribute name="Size" fixed="32" type="SizeType"/>
+                <xs:attribute name="Min" type="PointBound" use="optional"/>
+                <xs:attribute name="Max" type="PointBound" use="optional"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="FloatingPointParameter" type="FloatingPointParameterType"/>
+    <xs:complexType name="BitParameterType">
+        <xs:attributeGroup ref="Nameable"/>
+        <xs:attribute name="Size" use="required">
+            <xs:simpleType>
+                <xs:restriction base="xs:positiveInteger">
+                    <xs:maxInclusive value="64"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+        <xs:attribute name="Pos" use="required">
+            <xs:simpleType>
+                <xs:restriction base="xs:nonNegativeInteger">
+                    <xs:maxInclusive value="63"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+        <xs:attribute name="Max" type="xs:integer" use="optional"/>
+    </xs:complexType>
+    <xs:element name="BitParameterBlock">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="BitParameter" type="BitParameterType" maxOccurs="unbounded"/>
+            </xs:sequence>
+            <xs:attributeGroup ref="Nameable"/>
+            <xs:attribute name="Size" type="SizeType64" use="required"/>
+            <xs:attribute name="Mapping" type="xs:string" use="optional"/>
+        </xs:complexType>
+        <xs:unique name="BitParameterBlockSubElementsUniqueness">
+            <xs:selector xpath="*"/>
+            <xs:field xpath="@Name"/>
+        </xs:unique>
+    </xs:element>
+    <xs:element name="StringParameter">
+        <xs:complexType>
+            <xs:attributeGroup ref="Nameable"/>
+            <xs:attribute name="Mapping" type="xs:string" use="optional"/>
+            <xs:attribute name="MaxLength" type="xs:nonNegativeInteger" use="required"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:group name="ParameterBlockGroup">
+        <xs:choice>
+            <xs:element ref="BooleanParameter"/>
+            <xs:element ref="IntegerParameter"/>
+            <xs:element ref="EnumParameter"/>
+            <xs:element ref="FixedPointParameter"/>
+            <xs:element ref="FloatingPointParameter"/>
+            <xs:element ref="BitParameterBlock"/>
+            <xs:element ref="StringParameter"/>
+            <xs:element name="Component" type="ComponentInstance"/>
+            <xs:element name="ParameterBlock" type="ParameterBlockType">
+                <xs:unique name="ParameterBlockSubElementsUniqueness">
+                    <xs:selector xpath="*"/>
+                    <xs:field xpath="@Name"/>
+                </xs:unique>
+            </xs:element>
+        </xs:choice>
+    </xs:group>
+    <xs:complexType name="ParameterBlockType">
+        <xs:sequence>
+            <xs:group ref="ParameterBlockGroup" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attributeGroup ref="Nameable"/>
+        <xs:attributeGroup ref="ArrayLengthAttribute"/>
+        <xs:attribute name="Mapping" type="xs:string" use="optional"/>
+    </xs:complexType>
+    <!-- END Parameter.xsd -->
+
+    <!-- BEGIN ComponentTypeSet.xsd -->
+    <xs:complexType name="ComponentType">
+        <xs:sequence>
+            <xs:sequence>
+                <xs:group ref="ParameterBlockGroup"/>
+            </xs:sequence>
+        </xs:sequence>
+        <xs:attributeGroup ref="Nameable"/>
+        <xs:attribute name="Extends" use="optional" type="xs:NMTOKEN"/>
+        <xs:attribute name="Mapping" use="optional" type="xs:string"/>
+    </xs:complexType>
+    <xs:group name="ComponentTypeSetGroup">
+        <xs:choice>
+            <xs:element name="ComponentLibrary" type="ComponentTypeSetType"/>
+            <xs:element name="ComponentTypeSet" type="ComponentTypeSetType"/>
+            <xs:element name="ComponentType" type="ComponentType">
+                <xs:unique name="ComponentTypeSubElementsUniqueness">
+                    <xs:selector xpath="*"/>
+                    <xs:field xpath="@Name"/>
+                </xs:unique>
+            </xs:element>
+        </xs:choice>
+    </xs:group>
+    <xs:complexType name="ComponentTypeSetType">
+        <xs:sequence>
+            <xs:group ref="ComponentTypeSetGroup" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute ref="xml:base"/>
+    </xs:complexType>
+    <xs:element name="ComponentTypeSet" type="ComponentTypeSetType">
+        <xs:unique name="ComponentTypeUniquenessInComponentTypeSet">
+            <xs:selector xpath=".//ComponentType"/>
+            <xs:field xpath="@Name"/>
+        </xs:unique>
+    </xs:element>
+    <!-- END ComponentTypeSet.xsd -->
+
+    <!-- BEGIN ComponentLibrary.xsd -->
+    <xs:element name="ComponentLibrary" type="ComponentTypeSetType">
+        <xs:key name="ComponentTypeUniqueness">
+            <xs:selector xpath=".//ComponentType"/>
+            <xs:field xpath="@Name"/>
+        </xs:key>
+        <xs:keyref name="ComponentTypeNotFound" refer="ComponentTypeUniqueness">
+            <xs:selector xpath=".//ComponentType/Component"/>
+            <xs:field xpath="@Type"/>
+        </xs:keyref>
+    </xs:element>
+    <!-- END ComponentLibrary.xsd -->
+
+    <!-- BEGIN Subsystem.xsd -->
+    <xs:complexType name="SubsystemType">
+        <xs:sequence>
+            <xs:element ref="ComponentLibrary"/>
+            <xs:element name="InstanceDefinition">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:sequence>
+                            <xs:group ref="ParameterBlockGroup"/>
+                        </xs:sequence>
+                    </xs:sequence>
+                </xs:complexType>
+                <xs:unique name="InstanceDefinitionSubElementsUniqueness">
+                    <xs:selector xpath="*"/>
+                    <xs:field xpath="@Name"/>
+                </xs:unique>
+            </xs:element>
+        </xs:sequence>
+        <xs:attributeGroup ref="Nameable"/>
+        <xs:attribute name="Type" use="required" type="xs:NMTOKEN"/>
+        <xs:attribute name="Mapping" use="optional" type="xs:string"/>
+    </xs:complexType>
+    <xs:element name="Subsystem" type="SubsystemType">
+        <xs:keyref name="InstanceDefinitionComponentTypeNotFound" refer="ComponentTypeUniqueness">
+            <xs:selector xpath="InstanceDefinition/Component"/>
+            <xs:field xpath="@Type"/>
+        </xs:keyref>
+    </xs:element>
+    <!-- END Subsystem.xsd -->
+
+    <!-- BEGIN FileIncluder.xsd -->
+    <xs:complexType name="FileIncluderType">
+        <xs:annotation>
+            <xs:documentation>Element type used to import a root element from a file.</xs:documentation>
+        </xs:annotation>
+        <xs:attribute name="Path" type="xs:anyURI" use="required">
+            <xs:annotation>
+                <xs:documentation>Path to the file to import.
+This path may be absolute or relative to the path of the includer file.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+    <!-- END FileIncluder.xsd -->
+
+    <!-- BEGIN SystemClass.xsd -->
+    <xs:element name="SystemClass">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:choice maxOccurs="unbounded">
+                    <xs:element name="SubsystemInclude" type="FileIncluderType"/>
+                    <xs:element ref="Subsystem"/>
+                </xs:choice>
+            </xs:sequence>
+            <xs:attribute name="Name" type="xs:NMTOKEN" use="required"/>
+        </xs:complexType>
+    </xs:element>
+    <!-- END SystemClass.xsd -->
+
+    <!-- BEGIN ParameterFrameworkConfiguration.xsd -->
+    <xs:complexType name="ConfigurationFilePath">
+        <xs:attribute name="Path" type="xs:anyURI" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="PluginFile">
+        <xs:attribute name="Name" type="xs:NMTOKEN" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="PluginLocation">
+            <xs:sequence>
+                <xs:element name="Plugin" type="PluginFile" maxOccurs="unbounded" minOccurs="0"/>
+            </xs:sequence>
+            <xs:attribute name="Folder" type="xs:anyURI" use="required"/>
+    </xs:complexType>
+    <xs:element name="SubsystemPlugins">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="Location" type="PluginLocation" maxOccurs="unbounded" minOccurs="0"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:complexType name="SettingsConfigurationType">
+        <xs:sequence>
+            <xs:element name="ConfigurableDomainsFileLocation" type="ConfigurationFilePath"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:element name="ParameterFrameworkConfiguration">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element ref="SubsystemPlugins" />
+                <xs:element name="StructureDescriptionFileLocation" type="ConfigurationFilePath"/>
+                <xs:element name="SettingsConfiguration" type="SettingsConfigurationType" minOccurs="0"/>
+            </xs:sequence>
+            <xs:attribute name="SystemClassName" use="required" type="xs:NMTOKEN"/>
+            <xs:attribute name="ServerPort" use="required" type="xs:string"/>
+            <xs:attribute name="TuningAllowed" use="required" type="xs:boolean"/>
+        </xs:complexType>
+    </xs:element>
+    <!-- END ParameterFrameworkConfiguration.xsd -->
+</xs:schema>
diff --git a/audio/policy/1.0/xml/pfw_schemas/Android.bp b/audio/policy/1.0/xml/pfw_schemas/Android.bp
new file mode 100644
index 0000000..8054dc5
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/Android.bp
@@ -0,0 +1,107 @@
+xsd_config {
+    name: "audio_policy_engine_configurable_configuration_V1_0",
+    srcs: ["AllSchemas.xsd"],
+    package_name: "audio.policy.configurable.V1_0",
+}
+
+// Unfortunately, all rules only have a single output, thus
+// it is needed to create a rule per XSD file.
+
+genrule {
+    name: "audio_policy_engine_configurable_configuration_ComponentLibrary_V1_0",
+    srcs: ["ComponentLibrary.xsd"],
+    out: [
+        "audio_policy_engine_configurable_configuration_ComponentLibrary_V1_0.xsd",
+    ],
+    cmd: "cp -f $(in) $(genDir)/audio_policy_engine_configurable_configuration_ComponentLibrary_V1_0.xsd",
+}
+
+genrule {
+    name: "audio_policy_engine_configurable_configuration_ComponentTypeSet_V1_0",
+    srcs: ["ComponentTypeSet.xsd"],
+    out: [
+        "audio_policy_engine_configurable_configuration_ComponentTypeSet_V1_0.xsd",
+    ],
+    cmd: "cp -f $(in) $(genDir)/audio_policy_engine_configurable_configuration_ComponentTypeSet_V1_0.xsd",
+}
+
+genrule {
+    name: "audio_policy_engine_configurable_configuration_ConfigurableDomain_V1_0",
+    srcs: ["ConfigurableDomain.xsd"],
+    out: [
+        "audio_policy_engine_configurable_configuration_ConfigurableDomain_V1_0.xsd",
+    ],
+    cmd: "cp -f $(in) $(genDir)/audio_policy_engine_configurable_configuration_ConfigurableDomain_V1_0.xsd",
+}
+
+genrule {
+    name: "audio_policy_engine_configurable_configuration_ConfigurableDomains_V1_0",
+    srcs: ["ConfigurableDomains.xsd"],
+    out: [
+        "audio_policy_engine_configurable_configuration_ConfigurableDomains_V1_0.xsd",
+    ],
+    cmd: "cp -f $(in) $(genDir)/audio_policy_engine_configurable_configuration_ConfigurableDomains_V1_0.xsd",
+}
+
+genrule {
+    name: "audio_policy_engine_configurable_configuration_FileIncluder_V1_0",
+    srcs: ["FileIncluder.xsd"],
+    out: [
+        "audio_policy_engine_configurable_configuration_FileIncluder_V1_0.xsd",
+    ],
+    cmd: "cp -f $(in) $(genDir)/audio_policy_engine_configurable_configuration_FileIncluder_V1_0.xsd",
+}
+
+genrule {
+    name: "audio_policy_engine_configurable_configuration_Parameter_V1_0",
+    srcs: ["Parameter.xsd"],
+    out: [
+        "audio_policy_engine_configurable_configuration_Parameter_V1_0.xsd",
+    ],
+    cmd: "cp -f $(in) $(genDir)/audio_policy_engine_configurable_configuration_Parameter_V1_0.xsd",
+}
+
+genrule {
+    name: "audio_policy_engine_configurable_configuration_ParameterFrameworkConfiguration_V1_0",
+    srcs: ["ParameterFrameworkConfiguration.xsd"],
+    out: [
+        "audio_policy_engine_configurable_configuration_ParameterFrameworkConfiguration_V1_0.xsd",
+    ],
+    cmd: "cp -f $(in) $(genDir)/audio_policy_engine_configurable_configuration_ParameterFrameworkConfiguration_V1_0.xsd",
+}
+
+genrule {
+    name: "audio_policy_engine_configurable_configuration_ParameterSettings_V1_0",
+    srcs: ["ParameterSettings.xsd"],
+    out: [
+        "audio_policy_engine_configurable_configuration_ParameterSettings_V1_0.xsd",
+    ],
+    cmd: "cp -f $(in) $(genDir)/audio_policy_engine_configurable_configuration_ParameterSettings_V1_0.xsd",
+}
+
+genrule {
+    name: "audio_policy_engine_configurable_configuration_Subsystem_V1_0",
+    srcs: ["Subsystem.xsd"],
+    out: [
+        "audio_policy_engine_configurable_configuration_Subsystem_V1_0.xsd",
+    ],
+    cmd: "cp -f $(in) $(genDir)/audio_policy_engine_configurable_configuration_Subsystem_V1_0.xsd",
+}
+
+genrule {
+    name: "audio_policy_engine_configurable_configuration_SystemClass_V1_0",
+    srcs: ["SystemClass.xsd"],
+    out: [
+        "audio_policy_engine_configurable_configuration_SystemClass_V1_0.xsd",
+    ],
+    cmd: "cp -f $(in) $(genDir)/audio_policy_engine_configurable_configuration_SystemClass_V1_0.xsd",
+}
+
+genrule {
+    name: "audio_policy_engine_configurable_configuration_W3cXmlAttributes_V1_0",
+    srcs: ["W3cXmlAttributes.xsd"],
+    out: [
+        "audio_policy_engine_configurable_configuration_W3cXmlAttributes_V1_0.xsd",
+    ],
+    cmd: "cp -f $(in) $(genDir)/audio_policy_engine_configurable_configuration_W3cXmlAttributes_V1_0.xsd",
+}
diff --git a/audio/policy/1.0/xml/pfw_schemas/ComponentLibrary.xsd b/audio/policy/1.0/xml/pfw_schemas/ComponentLibrary.xsd
new file mode 100644
index 0000000..fbd70af
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/ComponentLibrary.xsd
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="W3cXmlAttributes.xsd"/>
+    <xs:include schemaLocation="ComponentTypeSet.xsd"/>
+    <xs:element name="ComponentLibrary" type="ComponentTypeSetType">
+        <xs:key name="ComponentTypeUniqueness">
+            <xs:selector xpath=".//ComponentType"/>
+            <xs:field xpath="@Name"/>
+        </xs:key>
+        <xs:keyref name="ComponentTypeNotFound" refer="ComponentTypeUniqueness">
+            <xs:selector xpath=".//ComponentType/Component"/>
+            <xs:field xpath="@Type"/>
+        </xs:keyref>
+    </xs:element>
+</xs:schema>
diff --git a/audio/policy/1.0/xml/pfw_schemas/ComponentTypeSet.xsd b/audio/policy/1.0/xml/pfw_schemas/ComponentTypeSet.xsd
new file mode 100644
index 0000000..d3938b6
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/ComponentTypeSet.xsd
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="W3cXmlAttributes.xsd"/>
+    <xs:include schemaLocation="Parameter.xsd"/>
+    <xs:complexType name="ComponentType">
+        <xs:sequence>
+            <xs:sequence maxOccurs="unbounded">
+                <xs:group ref="ParameterBlockGroup"/>
+            </xs:sequence>
+        </xs:sequence>
+        <xs:attributeGroup ref="Nameable"/>
+        <xs:attribute name="Extends" use="optional" type="xs:NMTOKEN"/>
+        <xs:attribute name="Mapping" use="optional" type="xs:string"/>
+    </xs:complexType>
+    <xs:group name="ComponentTypeSetGroup">
+        <xs:choice>
+            <xs:element name="ComponentLibrary" type="ComponentTypeSetType"/>
+            <xs:element name="ComponentTypeSet" type="ComponentTypeSetType"/>
+            <xs:element name="ComponentType" type="ComponentType">
+                <xs:unique name="ComponentTypeSubElementsUniqueness">
+                    <xs:selector xpath="*"/>
+                    <xs:field xpath="@Name"/>
+                </xs:unique>
+            </xs:element>
+        </xs:choice>
+    </xs:group>
+    <xs:complexType name="ComponentTypeSetType">
+        <xs:sequence>
+            <xs:group ref="ComponentTypeSetGroup" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute ref="xml:base"/>
+    </xs:complexType>
+    <xs:element name="ComponentTypeSet" type="ComponentTypeSetType">
+        <xs:unique name="ComponentTypeUniquenessInComponentTypeSet">
+            <xs:selector xpath=".//ComponentType"/>
+            <xs:field xpath="@Name"/>
+        </xs:unique>
+    </xs:element>
+</xs:schema>
diff --git a/audio/policy/1.0/xml/pfw_schemas/ConfigurableDomain.xsd b/audio/policy/1.0/xml/pfw_schemas/ConfigurableDomain.xsd
new file mode 100644
index 0000000..583acdc
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/ConfigurableDomain.xsd
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
+    <xs:include schemaLocation="ParameterSettings.xsd"/>
+    <xs:complexType name="SelectionCriterionRuleType">
+        <xs:attribute name="SelectionCriterion" type="xs:NMTOKEN" use="required"/>
+        <xs:attribute name="MatchesWhen" use="required">
+            <xs:simpleType name="MatchesWhenEnum">
+                <xs:restriction base="xs:NMTOKEN">
+                    <xs:enumeration value="Is"/>
+                    <xs:enumeration value="IsNot"/>
+                    <xs:enumeration value="Includes"/>
+                    <xs:enumeration value="Excludes"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+        <xs:attribute name="Value" use="required" type="xs:NMTOKEN"/>
+    </xs:complexType>
+    <xs:group name="RuleGroup">
+        <xs:choice>
+            <xs:element name="CompoundRule" type="CompoundRuleType"/>
+            <xs:element name="SelectionCriterionRule" type="SelectionCriterionRuleType"/>
+        </xs:choice>
+    </xs:group>
+    <xs:complexType name="CompoundRuleType">
+        <xs:sequence>
+            <xs:group ref="RuleGroup" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute name="Type">
+            <xs:simpleType name="TypeEnum">
+                <xs:restriction base="xs:NMTOKEN">
+                    <xs:enumeration value="Any"/>
+                    <xs:enumeration value="All"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+    </xs:complexType>
+    <xs:complexType name="ConfigurationsType">
+        <xs:sequence>
+            <xs:element maxOccurs="unbounded" name="Configuration">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:element name="CompoundRule" type="CompoundRuleType" minOccurs="0" maxOccurs="1"/>
+                    </xs:sequence>
+                    <xs:attribute name="Name" use="required" type="xs:NCName"/>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:group name="ComponentGroup">
+        <xs:sequence>
+            <xs:group ref="ParameterBlockGroup"/>
+        </xs:sequence>
+    </xs:group>
+    <xs:complexType name="ComponentType">
+        <xs:sequence>
+            <xs:choice>
+                <xs:group ref="ComponentGroup" maxOccurs="unbounded"/>
+                <xs:element name="Subsystem" type="ComponentType" maxOccurs="unbounded"/>
+            </xs:choice>
+        </xs:sequence>
+        <xs:attribute name="Name" use="required" type="xs:NCName"/>
+    </xs:complexType>
+    <xs:complexType name="ConfigurableElementsType">
+        <xs:sequence>
+            <xs:element maxOccurs="unbounded" minOccurs="0" name="ConfigurableElement">
+                <xs:complexType>
+                    <xs:attribute name="Path" use="required">
+                        <xs:simpleType>
+                            <xs:restriction base="xs:anyURI">
+                                <xs:pattern value="/.*[^/]"/>
+                            </xs:restriction>
+                        </xs:simpleType>
+                    </xs:attribute>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="ConfigurableElementSettingsType">
+        <xs:choice>
+            <xs:element name="BitParameter" type="IntegerParameterType"/>
+            <xs:group ref="ComponentGroup"/>
+        </xs:choice>
+        <xs:attribute name="Path" use="required">
+            <xs:simpleType>
+                <xs:restriction base="xs:anyURI">
+                    <xs:pattern value="/.*[^/]"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+    </xs:complexType>
+    <xs:complexType name="SettingsType">
+        <xs:sequence>
+            <xs:element maxOccurs="unbounded" minOccurs="0" name="Configuration">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:element name="ConfigurableElement" minOccurs="0" maxOccurs="unbounded" type="ConfigurableElementSettingsType"/>
+                    </xs:sequence>
+                    <xs:attribute name="Name" use="required" type="xs:NCName"/>
+                </xs:complexType>
+                <xs:unique name="ConfigurableElementUniqueness">
+                    <xs:selector xpath="ConfigurableElement"/>
+                    <xs:field xpath="@Path"/>
+                </xs:unique>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="ConfigurableDomainType">
+        <xs:sequence>
+            <xs:element name="Configurations" type="ConfigurationsType"/>
+            <xs:element name="ConfigurableElements" type="ConfigurableElementsType"/>
+            <xs:element name="Settings" type="SettingsType" minOccurs="0"/>
+        </xs:sequence>
+        <xs:attribute name="Name" use="required" type="xs:NCName"/>
+        <xs:attribute name="SequenceAware" use="optional" type="xs:boolean" default="false"/>
+    </xs:complexType>
+    <xs:element name="ConfigurableDomain" type="ConfigurableDomainType"/>
+</xs:schema>
diff --git a/audio/policy/1.0/xml/pfw_schemas/ConfigurableDomains.xsd b/audio/policy/1.0/xml/pfw_schemas/ConfigurableDomains.xsd
new file mode 100644
index 0000000..4fbe07a
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/ConfigurableDomains.xsd
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
+    <xs:include schemaLocation="ConfigurableDomain.xsd"/>
+    <xs:element name="ConfigurableDomains">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element maxOccurs="unbounded" name="ConfigurableDomain" type="ConfigurableDomainType">
+                    <xs:key name="ConfigurableElementKey">
+                        <xs:selector xpath="ConfigurableElements/ConfigurableElement"/>
+                        <xs:field xpath="@Path"/>
+                    </xs:key>
+                    <xs:keyref refer="ConfigurableElementKey" name="ConfigurableDomainReference">
+                        <xs:selector xpath="Settings/Configuration/ConfigurableElement"/>
+                        <xs:field xpath="@Path"/>
+                    </xs:keyref>
+                    <xs:key name="ConfigurationKey">
+                        <xs:selector xpath="Configurations/Configuration"/>
+                        <xs:field xpath="@Name"/>
+                    </xs:key>
+                    <xs:keyref refer="ConfigurationKey" name="ConfigurationReference2">
+                        <xs:selector xpath="ConfigurableElements/ConfigurableElement/Configuration"/>
+                        <xs:field xpath="@Name"/>
+                    </xs:keyref>
+                    <xs:keyref refer="ConfigurationKey" name="ConfigurationReference">
+                        <xs:selector xpath="Settings/Configuration"/>
+                        <xs:field xpath="@Name"/>
+                    </xs:keyref>
+                </xs:element>
+            </xs:sequence>
+            <xs:attribute name="SystemClassName" use="required" type="xs:NCName"/>
+        </xs:complexType>
+        <xs:unique name="ConfigurableDomainUniqueness">
+            <xs:selector xpath="ConfigurableDomain"/>
+            <xs:field xpath="@Name"/>
+        </xs:unique>
+    </xs:element>
+</xs:schema>
diff --git a/audio/policy/1.0/xml/pfw_schemas/FileIncluder.xsd b/audio/policy/1.0/xml/pfw_schemas/FileIncluder.xsd
new file mode 100644
index 0000000..049c903
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/FileIncluder.xsd
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- edited with XMLSPY v2004 rel. 3 U (http://www.xmlspy.com) by Samuel Gravez (Siemens VDO S.A.S.) -->
+<xs:schema  xmlns:html="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
+    <xs:complexType name="FileIncluderType">
+        <xs:annotation>
+            <xs:documentation>Element type used to import a root element from a file.</xs:documentation>
+        </xs:annotation>
+        <xs:attribute name="Path" type="xs:anyURI" use="required">
+            <xs:annotation>
+                <xs:documentation>Path to the file to import.
+This path may be absolute or relative to the path of the includer file.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+</xs:schema>
diff --git a/audio/policy/1.0/xml/pfw_schemas/Parameter.xsd b/audio/policy/1.0/xml/pfw_schemas/Parameter.xsd
new file mode 100644
index 0000000..b385e6e
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/Parameter.xsd
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
+    <xs:attributeGroup name="Nameable">
+        <xs:attribute name="Name" type="xs:NMTOKEN" use="required"/>
+        <xs:attribute name="Description" type="xs:string" use="optional"/>
+    </xs:attributeGroup>
+    <xs:attributeGroup name="TypedNameable">
+        <xs:attributeGroup ref="Nameable"/>
+        <xs:attribute name="Type" type="xs:NMTOKEN" use="required"/>
+    </xs:attributeGroup>
+    <xs:complexType name="ComponentInstance">
+        <xs:attributeGroup ref="TypedNameable"/>
+        <xs:attributeGroup ref="ArrayLengthAttribute"/>
+        <xs:attribute name="Mapping" use="optional" type="xs:string"/>
+    </xs:complexType>
+    <xs:simpleType name="SizeType">
+        <xs:restriction base="xs:positiveInteger">
+            <xs:pattern value="8|16|32"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="SizeType64">
+        <xs:restriction base="xs:positiveInteger">
+            <xs:pattern value="8|16|32|64"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:attributeGroup name="IntegerParameterAttributes">
+        <xs:attribute name="Size" type="SizeType" use="required"/>
+        <xs:attribute name="Min" type="xs:integer" use="optional"/>
+        <xs:attribute name="Max" type="xs:integer" use="optional"/>
+        <xs:attribute name="Signed" type="xs:boolean" use="optional" default="false"/>
+    </xs:attributeGroup>
+    <xs:attributeGroup name="ArrayLengthAttribute">
+        <xs:attribute name="ArrayLength" type="xs:nonNegativeInteger" use="optional" default="0"/>
+    </xs:attributeGroup>
+    <xs:complexType name="Adaptation">
+        <xs:attribute name="Offset" type="xs:integer" default="0"/>
+    </xs:complexType>
+    <xs:complexType name="LinearAdaptationType">
+        <xs:complexContent>
+            <xs:extension base="Adaptation">
+                <xs:attribute name="SlopeNumerator" type="xs:double" default="1"/>
+                <xs:attribute name="SlopeDenominator" type="xs:double" default="1"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="LinearAdaptation" type="LinearAdaptationType"/>
+    <xs:element name="LogarithmicAdaptation">
+        <xs:complexType>
+            <xs:complexContent>
+                <xs:extension base="LinearAdaptationType">
+                    <xs:attribute name="LogarithmBase" type="xs:double" default="10"/>
+                    <xs:attribute name="FloorValue" type="xs:double" default="-INF"/>
+                </xs:extension>
+            </xs:complexContent>
+        </xs:complexType>
+    </xs:element>
+    <xs:complexType name="Parameter" abstract="true">
+        <xs:attributeGroup ref="Nameable"/>
+        <xs:attribute name="Mapping" type="xs:string" use="optional"/>
+        <xs:attributeGroup ref="ArrayLengthAttribute"/>
+    </xs:complexType>
+    <xs:element name="BooleanParameter">
+        <xs:complexType>
+            <xs:complexContent>
+                <xs:extension base="Parameter">
+                    <xs:attribute name="Size" fixed="8" type="SizeType"/>
+                </xs:extension>
+            </xs:complexContent>
+        </xs:complexType>
+    </xs:element>
+    <xs:complexType name="IntegerParameterType">
+        <xs:complexContent>
+            <xs:extension base="Parameter">
+                <xs:choice minOccurs="0">
+                    <xs:element ref="LinearAdaptation"/>
+                    <xs:element ref="LogarithmicAdaptation"/>
+                </xs:choice>
+                <xs:attributeGroup ref="IntegerParameterAttributes"/>
+                <xs:attribute name="Unit" type="xs:token" use="optional"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="IntegerParameter" type="IntegerParameterType"/>
+    <xs:complexType name="EnumParameterType">
+        <xs:complexContent>
+            <xs:extension base="Parameter">
+                <xs:sequence>
+                    <xs:element name="ValuePair" maxOccurs="unbounded">
+                        <xs:complexType>
+                            <xs:attribute name="Literal" type="xs:string" use="required"/>
+                            <xs:attribute name="Numerical" use="required">
+                                <xs:simpleType>
+                                    <xs:restriction base="xs:string">
+                                        <xs:pattern value="0|[+-]?[1-9][0-9]*"/>
+                                        <xs:pattern value="0x[0-9a-fA-F]+"/>
+                                    </xs:restriction>
+                                </xs:simpleType>
+                            </xs:attribute>
+                        </xs:complexType>
+                    </xs:element>
+                </xs:sequence>
+                <xs:attribute name="Size" type="SizeType" use="required"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="EnumParameter" type="EnumParameterType">
+        <xs:unique name="LiteralUniqueness">
+            <xs:selector xpath="ValuePair"/>
+            <xs:field xpath="@Literal"/>
+        </xs:unique>
+        <xs:unique name="NumericalUniqueness">
+            <xs:selector xpath="ValuePair"/>
+            <xs:field xpath="@Numerical"/>
+        </xs:unique>
+    </xs:element>
+    <xs:simpleType name="PointBound">
+        <xs:restriction base="xs:string">
+            <xs:pattern value="(0|[+-]?0\.[0-9]+|(([+-]?[1-9][0-9]*)(\.[0-9]+)?))([Ee][+-]?[0-9]+)?"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="PointParameterType">
+        <xs:complexContent>
+            <xs:extension base="Parameter">
+                <xs:attribute name="Unit" type="xs:token" use="optional"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:complexType name="FixedPointParameterType">
+        <xs:complexContent>
+            <xs:extension base="PointParameterType">
+                <xs:attribute name="Size" type="SizeType" use="required"/>
+                <xs:attribute name="Integral" type="xs:nonNegativeInteger" use="required"/>
+                <xs:attribute name="Fractional" type="xs:nonNegativeInteger" use="required"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="FixedPointParameter" type="FixedPointParameterType"/>
+    <xs:complexType name="FloatingPointParameterType">
+        <xs:complexContent>
+            <xs:extension base="PointParameterType">
+                <xs:attribute name="Size" fixed="32" type="SizeType"/>
+                <xs:attribute name="Min" type="PointBound" use="optional"/>
+                <xs:attribute name="Max" type="PointBound" use="optional"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="FloatingPointParameter" type="FloatingPointParameterType"/>
+    <xs:complexType name="BitParameterType">
+        <xs:attributeGroup ref="Nameable"/>
+        <xs:attribute name="Size" use="required">
+            <xs:simpleType>
+                <xs:restriction base="xs:positiveInteger">
+                    <xs:maxInclusive value="64"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+        <xs:attribute name="Pos" use="required">
+            <xs:simpleType>
+                <xs:restriction base="xs:nonNegativeInteger">
+                    <xs:maxInclusive value="63"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+        <xs:attribute name="Max" type="xs:integer" use="optional"/>
+    </xs:complexType>
+    <xs:element name="BitParameterBlock">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="BitParameter" type="BitParameterType" maxOccurs="unbounded"/>
+            </xs:sequence>
+            <xs:attributeGroup ref="Nameable"/>
+            <xs:attribute name="Size" type="SizeType64" use="required"/>
+            <xs:attribute name="Mapping" type="xs:string" use="optional"/>
+        </xs:complexType>
+        <xs:unique name="BitParameterBlockSubElementsUniqueness">
+            <xs:selector xpath="*"/>
+            <xs:field xpath="@Name"/>
+        </xs:unique>
+    </xs:element>
+    <xs:element name="StringParameter">
+        <xs:complexType>
+            <xs:attributeGroup ref="Nameable"/>
+            <xs:attribute name="Mapping" type="xs:string" use="optional"/>
+            <xs:attribute name="MaxLength" type="xs:nonNegativeInteger" use="required"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:group name="ParameterBlockGroup">
+        <xs:choice>
+            <xs:element ref="BooleanParameter"/>
+            <xs:element ref="IntegerParameter"/>
+            <xs:element ref="EnumParameter"/>
+            <xs:element ref="FixedPointParameter"/>
+            <xs:element ref="FloatingPointParameter"/>
+            <xs:element ref="BitParameterBlock"/>
+            <xs:element ref="StringParameter"/>
+            <xs:element name="Component" type="ComponentInstance"/>
+            <xs:element name="ParameterBlock" type="ParameterBlockType">
+                <xs:unique name="ParameterBlockSubElementsUniqueness">
+                    <xs:selector xpath="*"/>
+                    <xs:field xpath="@Name"/>
+                </xs:unique>
+            </xs:element>
+        </xs:choice>
+    </xs:group>
+    <xs:complexType name="ParameterBlockType">
+        <xs:sequence>
+            <xs:group ref="ParameterBlockGroup" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attributeGroup ref="Nameable"/>
+        <xs:attributeGroup ref="ArrayLengthAttribute"/>
+        <xs:attribute name="Mapping" type="xs:string" use="optional"/>
+    </xs:complexType>
+</xs:schema>
diff --git a/audio/policy/1.0/xml/pfw_schemas/ParameterFrameworkConfiguration.xsd b/audio/policy/1.0/xml/pfw_schemas/ParameterFrameworkConfiguration.xsd
new file mode 100644
index 0000000..d796ab3
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/ParameterFrameworkConfiguration.xsd
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--W3C Schema generated by XMLSpy v2011 sp1 (http://www.altova.com)-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <xs:complexType name="ConfigurationFilePath">
+        <xs:attribute name="Path" type="xs:anyURI" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="PluginFile">
+        <xs:attribute name="Name" type="xs:NMTOKEN" use="required"/>
+    </xs:complexType> 
+    <xs:complexType name="PluginLocation">
+            <xs:sequence>
+                <xs:element name="Plugin" type="PluginFile" maxOccurs="unbounded" minOccurs="0"/>
+            </xs:sequence>
+            <xs:attribute name="Folder" type="xs:anyURI" use="required"/>
+    </xs:complexType>
+    <xs:element name="SubsystemPlugins">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="Location" type="PluginLocation" maxOccurs="unbounded" minOccurs="0"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:complexType name="SettingsConfigurationType">
+        <xs:sequence>
+            <xs:element name="ConfigurableDomainsFileLocation" type="ConfigurationFilePath"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:element name="ParameterFrameworkConfiguration">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element ref="SubsystemPlugins" />
+                <xs:element name="StructureDescriptionFileLocation" type="ConfigurationFilePath"/>
+                <xs:element name="SettingsConfiguration" type="SettingsConfigurationType" minOccurs="0"/>
+            </xs:sequence>
+            <xs:attribute name="SystemClassName" use="required" type="xs:NMTOKEN"/>
+            <xs:attribute name="ServerPort" use="required" type="xs:string"/>
+            <xs:attribute name="TuningAllowed" use="required" type="xs:boolean"/>
+        </xs:complexType>
+    </xs:element>
+</xs:schema>
diff --git a/audio/policy/1.0/xml/pfw_schemas/ParameterSettings.xsd b/audio/policy/1.0/xml/pfw_schemas/ParameterSettings.xsd
new file mode 100644
index 0000000..8951b38
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/ParameterSettings.xsd
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
+    <xs:complexType name="ParameterType" abstract="true">
+        <xs:simpleContent>
+            <xs:extension base="xs:string">
+                <xs:attribute name="Name" type="xs:NMTOKEN" use="required"/>
+                <xs:attribute name="ValueSpace" use="optional">
+                    <xs:simpleType name="ValueSpaceEnum">
+                        <xs:restriction base="xs:string">
+                            <xs:enumeration value="Raw"/>
+                            <xs:enumeration value="Real"/>
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:attribute>
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+    <xs:complexType name="BooleanParameterType">
+        <xs:simpleContent>
+            <xs:restriction base="ParameterType">
+                <xs:pattern value="([01][\s]*)+"/>
+                <xs:pattern value="((0x0|0x1)[\s]*)+"/>
+                <xs:attribute name="ValueSpace" use="prohibited"/>
+            </xs:restriction>
+        </xs:simpleContent>
+    </xs:complexType>
+    <xs:complexType name="IntegerParameterType">
+        <xs:simpleContent>
+            <xs:restriction base="ParameterType">
+                <xs:pattern value="(0|([+-]?[1-9][0-9]*))(\s+(0|([+-]?[1-9][0-9]*)))*"/>
+                <xs:pattern value="(0x[0-9a-fA-F]+)(\s+(0x[0-9a-fA-F]+))*"/>
+                <xs:attribute name="ValueSpace" use="prohibited"/>
+            </xs:restriction>
+        </xs:simpleContent>
+    </xs:complexType>
+    <xs:complexType name="EnumParameterType">
+        <xs:simpleContent>
+            <xs:restriction base="ParameterType">
+                <xs:attribute name="ValueSpace" use="prohibited"/>
+            </xs:restriction>
+        </xs:simpleContent>
+    </xs:complexType>
+    <xs:complexType name="PointParameterType">
+        <xs:simpleContent>
+            <xs:restriction base="ParameterType">
+                <xs:pattern value="((0|[+-]?0\.[0-9]+|(([+-]?[1-9][0-9]*)(\.[0-9]+)?))([Ee][+-]?[0-9]+)?)(\s+(0|[+-]?0\.[0-9]+|(([+-]?[1-9][0-9]*)(\.[0-9]+)?))([Ee][+-]?[0-9]+)?)*"/>
+                <xs:pattern value="(0x[0-9a-fA-F]+)(\s+(0x[0-9a-fA-F]+))*"/>
+            </xs:restriction>
+        </xs:simpleContent>
+    </xs:complexType>
+    <xs:complexType name="BitParameterBlockType">
+        <xs:sequence>
+            <xs:element name="BitParameter" maxOccurs="unbounded" type="IntegerParameterType"/>
+        </xs:sequence>
+        <xs:attribute name="Name" type="xs:NMTOKEN" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="StringParameterType">
+        <xs:simpleContent>
+            <xs:extension base="xs:string">
+                <xs:attribute name="Name" type="xs:NMTOKEN" use="required"/>
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+    <xs:group name="ParameterBlockGroup">
+        <xs:choice>
+            <xs:element name="BooleanParameter" type="BooleanParameterType"/>
+            <xs:element name="IntegerParameter" type="IntegerParameterType"/>
+            <xs:element name="EnumParameter" type="EnumParameterType"/>
+            <xs:element name="FixedPointParameter" type="PointParameterType"/>
+            <xs:element name="FloatingPointParameter" type="PointParameterType"/>
+            <xs:element name="BitParameterBlock" type="BitParameterBlockType">
+                <xs:unique name="BitParameterBlockSubElementsUniqueness">
+                    <xs:selector xpath="*"/>
+                    <xs:field xpath="@Name"/>
+                </xs:unique>
+            </xs:element>
+            <xs:element name="StringParameter" type="StringParameterType"/>
+            <xs:element name="Component" type="ParameterBlockType"/>
+            <xs:element name="ParameterBlock" type="ParameterBlockType">
+                <xs:unique name="ParameterBlockSubElementsUniqueness">
+                    <xs:selector xpath="*"/>
+                    <xs:field xpath="@Name"/>
+                </xs:unique>
+            </xs:element>
+        </xs:choice>
+    </xs:group>
+    <xs:complexType name="ParameterBlockType">
+        <xs:sequence>
+            <xs:group ref="ParameterBlockGroup" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute name="Name" type="xs:NMTOKEN" use="required"/>
+    </xs:complexType>
+</xs:schema>
diff --git a/audio/policy/1.0/xml/pfw_schemas/README.md b/audio/policy/1.0/xml/pfw_schemas/README.md
new file mode 100644
index 0000000..243b5c0
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/README.md
@@ -0,0 +1,87 @@
+# parameter-framework configuration file XML Schemas
+
+These are W3C Schemas for the various configuration files.
+
+`xmllint` may be used to check for correctness, e.g:
+
+    xmllint --xinclude --noout --schema ParameterFrameworkConfiguration.xsd /path/to/your/ParameterFrameworkConfiguration.xml
+
+See `tools/xmlValidator` for a custom alternative tool.
+
+Only `ParameterFrameworkConfiguration.xsd`, `SystemClass.xsd`, `Subsystem.xsd` and
+`ConfigurableDomains.xsd` are relevant for use with xmllint: the others are
+included by these 4 XSDs.
+
+**You may refer to samples at
+<https://github.com/01org/parameter-framework-samples>.**
+
+## ParameterFrameworkConfiguration.xsd
+
+Schema for the **top-level configuration**.  It contains:
+
+- A reference to the `SystemClass` (aka StructureDescription) XML file (see
+  below);
+- The list of plugins (libraries) to be used. They may be split according to
+the folder they reside in. The `Folder` attribute can either be:
+
+    - an absolute path,
+    - a relative path (relative to the execution directory),
+    - empty.
+
+    In the first two cases, the runtime loader will be asked to explicitely load
+    the libraries found in the specified folder; in the last case (empty string)
+    the runtime loader will search for the library on its own (e.g. on Linux
+    distribution this is usually `/lib`, `/usr/lib` - see `man ld.so`)
+- Optionally, a reference to the `Settings`.
+
+Attributes of `ParameterFrameworkConfiguration` are:
+
+- The `SystemClass` name (for consistency check)
+- `TuningAllowed` (whether the parameter-framework listens for commands)
+- The `ServerPort` bind Address (PATH or TCP port) on which the parameter-framework listens if
+  `TuningAllowed=true`.
+
+## SystemClass.xsd
+
+Schema for the **SystemClass associated with the top-level configuration**.  It
+points to all the "Subsystem" files (see below).
+
+The `Name` attribute of the SystemClass must match the `SystemClass` attribute
+of the top-level configuration file. This name will be the first component of
+all parameters in it, i.e. if its name is "FooBar", its path is `/FooBar`. We
+will use this name in examples below.
+
+## Subsystem.xsd
+
+Schema for all **Subsystem files** (aka Structure files).  These files describe the
+content and structure of the system to be managed by the parameter-framework
+and also indicate which plugin is to be used.
+
+A Subsystem has the following attribute:
+
+- `Name` (self-explanatory); again it is the base component of all parameters
+  inside it; i.e. if its name is "Spam", its path is `/FooBar/Spam`;
+- `Type`, which indicates which SubsystemBuilder is to be used (each plugin can
+  declare one or more SubsystemBuilders); it may be defined as `Virtual`, in
+  which case, no plugin will be used and the parameters won't be synchronized.
+  This is useful for debugging but may also be used for the parameter-framework
+  to act as a configurable settings database;
+- `Mapping` (optional), defines a Mapping to be inherited by all Components in
+  the Subsystem.
+
+A Subsystem *must* contain:
+
+- A `ComponentLibrary`, which may include (using `<xi:include href="xyz.xml"/>`)
+  other files containing a `<ComponentLibrary>` or a `<ComponentTypeSet>` tag.
+- An `InstanceDefinition` which instantiates the parameters and may use
+  ComponentTypes defined in the ComponentLibrary.
+
+## ConfigurableDomains.xsd
+
+Schema for the ConfigurableDomains (aka Settings files).  These files contain
+the rules for applying values to parameters.
+
+Writing this file by hand is painful but it is not intended to be dealt
+with directly: instead, you may use the command-line interface (see
+`remote-process/README.md`) to set the settings and export the resulting
+Settings with the `getDomainsWithSettingsXML` command.
diff --git a/audio/policy/1.0/xml/pfw_schemas/Subsystem.xsd b/audio/policy/1.0/xml/pfw_schemas/Subsystem.xsd
new file mode 100644
index 0000000..b1bfcbc
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/Subsystem.xsd
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--W3C Schema generated by XMLSpy v2007 (http://www.altova.com)-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <xs:include schemaLocation="ComponentLibrary.xsd"/>
+    <xs:complexType name="SubsystemType">
+        <xs:sequence>
+            <xs:element ref="ComponentLibrary"/>
+            <xs:element name="InstanceDefinition">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:sequence maxOccurs="unbounded">
+                            <xs:group ref="ParameterBlockGroup"/>
+                        </xs:sequence>
+                    </xs:sequence>
+                </xs:complexType>
+                <xs:unique name="InstanceDefinitionSubElementsUniqueness">
+                    <xs:selector xpath="*"/>
+                    <xs:field xpath="@Name"/>
+                </xs:unique>
+            </xs:element>
+        </xs:sequence>
+        <xs:attributeGroup ref="Nameable"/>
+        <xs:attribute name="Type" use="required" type="xs:NMTOKEN"/>
+        <xs:attribute name="Mapping" use="optional" type="xs:string"/>
+    </xs:complexType>
+    <xs:element name="Subsystem" type="SubsystemType">
+        <xs:keyref name="InstanceDefinitionComponentTypeNotFound" refer="ComponentTypeUniqueness">
+            <xs:selector xpath="InstanceDefinition/Component"/>
+            <xs:field xpath="@Type"/>
+        </xs:keyref>
+    </xs:element>
+</xs:schema>
diff --git a/audio/policy/1.0/xml/pfw_schemas/SystemClass.xsd b/audio/policy/1.0/xml/pfw_schemas/SystemClass.xsd
new file mode 100644
index 0000000..d07793e
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/SystemClass.xsd
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--W3C Schema generated by XMLSpy v2007 (http://www.altova.com)-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <xs:include schemaLocation="FileIncluder.xsd"/>
+    <xs:include schemaLocation="Subsystem.xsd"/>
+    <xs:element name="SystemClass">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:choice maxOccurs="unbounded">
+                    <xs:element name="SubsystemInclude" type="FileIncluderType"/>
+                    <xs:element ref="Subsystem"/>
+                </xs:choice>
+            </xs:sequence>
+            <xs:attribute name="Name" type="xs:NMTOKEN" use="required"/>
+        </xs:complexType>
+    </xs:element>
+</xs:schema>
diff --git a/audio/policy/1.0/xml/pfw_schemas/W3cXmlAttributes.xsd b/audio/policy/1.0/xml/pfw_schemas/W3cXmlAttributes.xsd
new file mode 100644
index 0000000..7f9de1b
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/W3cXmlAttributes.xsd
@@ -0,0 +1,146 @@
+<?xml version='1.0'?>
+<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace" xmlns:xs="http://www.w3.org/2001/XMLSchema" xml:lang="en">
+
+ <xs:annotation>
+  <xs:documentation>
+   See http://www.w3.org/XML/1998/namespace.html and
+   http://www.w3.org/TR/REC-xml for information about this namespace.
+
+    This schema document describes the XML namespace, in a form
+    suitable for import by other schema documents.
+
+    Note that local names in this namespace are intended to be defined
+    only by the World Wide Web Consortium or its subgroups.  The
+    following names are currently defined in this namespace and should
+    not be used with conflicting semantics by any Working Group,
+    specification, or document instance:
+
+    base (as an attribute name): denotes an attribute whose value
+         provides a URI to be used as the base for interpreting any
+         relative URIs in the scope of the element on which it
+         appears; its value is inherited.  This name is reserved
+         by virtue of its definition in the XML Base specification.
+
+    id   (as an attribute name): denotes an attribute whose value
+         should be interpreted as if declared to be of type ID.
+         The xml:id specification is not yet a W3C Recommendation,
+         but this attribute is included here to facilitate experimentation
+         with the mechanisms it proposes.  Note that it is _not_ included
+         in the specialAttrs attribute group.
+
+    lang (as an attribute name): denotes an attribute whose value
+         is a language code for the natural language of the content of
+         any element; its value is inherited.  This name is reserved
+         by virtue of its definition in the XML specification.
+
+    space (as an attribute name): denotes an attribute whose
+         value is a keyword indicating what whitespace processing
+         discipline is intended for the content of the element; its
+         value is inherited.  This name is reserved by virtue of its
+         definition in the XML specification.
+
+    Father (in any context at all): denotes Jon Bosak, the chair of
+         the original XML Working Group.  This name is reserved by
+         the following decision of the W3C XML Plenary and
+         XML Coordination groups:
+
+             In appreciation for his vision, leadership and dedication
+             the W3C XML Plenary on this 10th day of February, 2000
+             reserves for Jon Bosak in perpetuity the XML name
+             xml:Father
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+  <xs:documentation>This schema defines attributes and an attribute group
+        suitable for use by
+        schemas wishing to allow xml:base, xml:lang, xml:space or xml:id
+        attributes on elements they define.
+
+        To enable this, such a schema must import this schema
+        for the XML namespace, e.g. as follows:
+        &lt;schema . . .>
+         . . .
+         &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+                    schemaLocation="http://www.w3.org/2005/08/xml.xsd"/>
+
+        Subsequently, qualified reference to any of the attributes
+        or the group defined below will have the desired effect, e.g.
+
+        &lt;type . . .>
+         . . .
+         &lt;attributeGroup ref="xml:specialAttrs"/>
+
+         will define a type which will schema-validate an instance
+         element with any of those attributes</xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+  <xs:documentation>In keeping with the XML Schema WG's standard versioning
+   policy, this schema document will persist at
+   http://www.w3.org/2005/08/xml.xsd.
+   At the date of issue it can also be found at
+   http://www.w3.org/2001/xml.xsd.
+   The schema document at that URI may however change in the future,
+   in order to remain compatible with the latest version of XML Schema
+   itself, or with the XML namespace itself.  In other words, if the XML
+   Schema or XML namespaces change, the version of this document at
+   http://www.w3.org/2001/xml.xsd will change
+   accordingly; the version at
+   http://www.w3.org/2005/08/xml.xsd will not change.
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:attribute name="lang">
+  <xs:annotation>
+   <xs:documentation>Attempting to install the relevant ISO 2- and 3-letter
+         codes as the enumerated possible values is probably never
+         going to be a realistic possibility.  See
+         RFC 3066 at http://www.ietf.org/rfc/rfc3066.txt and the IANA registry
+         at http://www.iana.org/assignments/lang-tag-apps.htm for
+         further information.
+
+         The union allows for the 'un-declaration' of xml:lang with
+         the empty string.</xs:documentation>
+  </xs:annotation>
+  <xs:simpleType name="langEnum">
+   <xs:union memberTypes="xs:language">
+    <xs:simpleType>
+     <xs:restriction base="xs:string">
+      <xs:enumeration value=""/>
+     </xs:restriction>
+    </xs:simpleType>
+   </xs:union>
+  </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="space">
+  <xs:simpleType name="spaceEnum">
+   <xs:restriction base="xs:NCName">
+    <xs:enumeration value="default"/>
+    <xs:enumeration value="preserve"/>
+   </xs:restriction>
+  </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="base" type="xs:anyURI">
+  <xs:annotation>
+   <xs:documentation>See http://www.w3.org/TR/xmlbase/ for
+                     information about this attribute.</xs:documentation>
+  </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="id" type="xs:ID">
+  <xs:annotation>
+   <xs:documentation>See http://www.w3.org/TR/xml-id/ for
+                     information about this attribute.</xs:documentation>
+  </xs:annotation>
+ </xs:attribute>
+
+ <xs:attributeGroup name="specialAttrs">
+  <xs:attribute ref="xml:base"/>
+  <xs:attribute ref="xml:lang"/>
+  <xs:attribute ref="xml:space"/>
+ </xs:attributeGroup>
+
+</xs:schema>
diff --git a/audio/policy/1.0/xml/pfw_schemas/api/current.txt b/audio/policy/1.0/xml/pfw_schemas/api/current.txt
new file mode 100644
index 0000000..c2fb6fc
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/api/current.txt
@@ -0,0 +1,495 @@
+// Signature format: 2.0
+package audio.policy.configurable.V1_0 {
+
+  public class Adaptation {
+    ctor public Adaptation();
+    method public java.math.BigInteger getOffset();
+    method public void setOffset(java.math.BigInteger);
+  }
+
+  public class BitParameterBlock {
+    ctor public BitParameterBlock();
+    method public java.util.List<audio.policy.configurable.V1_0.BitParameterType> getBitParameter();
+    method public String getDescription();
+    method public String getMapping();
+    method public String getName();
+    method public java.math.BigInteger getSize();
+    method public void setDescription(String);
+    method public void setMapping(String);
+    method public void setName(String);
+    method public void setSize(java.math.BigInteger);
+  }
+
+  public class BitParameterBlockType {
+    ctor public BitParameterBlockType();
+    method public java.util.List<audio.policy.configurable.V1_0.IntegerParameterType> getBitParameter();
+    method public String getName();
+    method public void setName(String);
+  }
+
+  public class BitParameterType {
+    ctor public BitParameterType();
+    method public String getDescription();
+    method public java.math.BigInteger getMax();
+    method public String getName();
+    method public java.math.BigInteger getPos();
+    method public java.math.BigInteger getSize();
+    method public void setDescription(String);
+    method public void setMax(java.math.BigInteger);
+    method public void setName(String);
+    method public void setPos(java.math.BigInteger);
+    method public void setSize(java.math.BigInteger);
+  }
+
+  public class BooleanParameter extends audio.policy.configurable.V1_0.Parameter {
+    ctor public BooleanParameter();
+    method public java.math.BigInteger getSize();
+    method public void setSize(java.math.BigInteger);
+  }
+
+  public class BooleanParameterType extends audio.policy.configurable.V1_0.ParameterType {
+    ctor public BooleanParameterType();
+  }
+
+  public class ComponentInstance {
+    ctor public ComponentInstance();
+    method public java.math.BigInteger getArrayLength();
+    method public String getDescription();
+    method public String getMapping();
+    method public String getName();
+    method public String getType();
+    method public void setArrayLength(java.math.BigInteger);
+    method public void setDescription(String);
+    method public void setMapping(String);
+    method public void setName(String);
+    method public void setType(String);
+  }
+
+  public class ComponentType {
+    ctor public ComponentType();
+    method public audio.policy.configurable.V1_0.BitParameterBlock getBitParameterBlock();
+    method public audio.policy.configurable.V1_0.BooleanParameter getBooleanParameter();
+    method public audio.policy.configurable.V1_0.ComponentInstance getComponent_optional();
+    method public String getDescription();
+    method public audio.policy.configurable.V1_0.EnumParameterType getEnumParameter();
+    method public audio.policy.configurable.V1_0.FixedPointParameterType getFixedPointParameter();
+    method public audio.policy.configurable.V1_0.FloatingPointParameterType getFloatingPointParameter();
+    method public audio.policy.configurable.V1_0.IntegerParameterType getIntegerParameter();
+    method public String getMapping();
+    method public String getName();
+    method public audio.policy.configurable.V1_0.ParameterBlockType getParameterBlock_optional();
+    method public audio.policy.configurable.V1_0.StringParameter getStringParameter();
+    method public String get_extends();
+    method public void setBitParameterBlock(audio.policy.configurable.V1_0.BitParameterBlock);
+    method public void setBooleanParameter(audio.policy.configurable.V1_0.BooleanParameter);
+    method public void setComponent_optional(audio.policy.configurable.V1_0.ComponentInstance);
+    method public void setDescription(String);
+    method public void setEnumParameter(audio.policy.configurable.V1_0.EnumParameterType);
+    method public void setFixedPointParameter(audio.policy.configurable.V1_0.FixedPointParameterType);
+    method public void setFloatingPointParameter(audio.policy.configurable.V1_0.FloatingPointParameterType);
+    method public void setIntegerParameter(audio.policy.configurable.V1_0.IntegerParameterType);
+    method public void setMapping(String);
+    method public void setName(String);
+    method public void setParameterBlock_optional(audio.policy.configurable.V1_0.ParameterBlockType);
+    method public void setStringParameter(audio.policy.configurable.V1_0.StringParameter);
+    method public void set_extends(String);
+  }
+
+  public class ComponentTypeSetType {
+    ctor public ComponentTypeSetType();
+    method public String getBase();
+    method public audio.policy.configurable.V1_0.ComponentTypeSetType getComponentLibrary_optional();
+    method public audio.policy.configurable.V1_0.ComponentTypeSetType getComponentTypeSet_optional();
+    method public audio.policy.configurable.V1_0.ComponentType getComponentType_optional();
+    method public void setBase(String);
+    method public void setComponentLibrary_optional(audio.policy.configurable.V1_0.ComponentTypeSetType);
+    method public void setComponentTypeSet_optional(audio.policy.configurable.V1_0.ComponentTypeSetType);
+    method public void setComponentType_optional(audio.policy.configurable.V1_0.ComponentType);
+  }
+
+  public class CompoundRuleType {
+    ctor public CompoundRuleType();
+    method public audio.policy.configurable.V1_0.CompoundRuleType getCompoundRule_optional();
+    method public audio.policy.configurable.V1_0.SelectionCriterionRuleType getSelectionCriterionRule_optional();
+    method public audio.policy.configurable.V1_0.TypeEnum getType();
+    method public void setCompoundRule_optional(audio.policy.configurable.V1_0.CompoundRuleType);
+    method public void setSelectionCriterionRule_optional(audio.policy.configurable.V1_0.SelectionCriterionRuleType);
+    method public void setType(audio.policy.configurable.V1_0.TypeEnum);
+  }
+
+  public class ConfigurableDomainType {
+    ctor public ConfigurableDomainType();
+    method public audio.policy.configurable.V1_0.ConfigurableElementsType getConfigurableElements();
+    method public audio.policy.configurable.V1_0.ConfigurationsType getConfigurations();
+    method public String getName();
+    method public boolean getSequenceAware();
+    method public audio.policy.configurable.V1_0.SettingsType getSettings();
+    method public void setConfigurableElements(audio.policy.configurable.V1_0.ConfigurableElementsType);
+    method public void setConfigurations(audio.policy.configurable.V1_0.ConfigurationsType);
+    method public void setName(String);
+    method public void setSequenceAware(boolean);
+    method public void setSettings(audio.policy.configurable.V1_0.SettingsType);
+  }
+
+  public class ConfigurableDomains {
+    ctor public ConfigurableDomains();
+    method public java.util.List<audio.policy.configurable.V1_0.ConfigurableDomainType> getConfigurableDomain();
+    method public String getSystemClassName();
+    method public void setSystemClassName(String);
+  }
+
+  public class ConfigurableElementSettingsType {
+    ctor public ConfigurableElementSettingsType();
+    method public audio.policy.configurable.V1_0.IntegerParameterType getBitParameter_optional();
+    method public String getPath();
+    method public void setBitParameter_optional(audio.policy.configurable.V1_0.IntegerParameterType);
+    method public void setPath(String);
+  }
+
+  public class ConfigurableElementsType {
+    ctor public ConfigurableElementsType();
+    method public java.util.List<audio.policy.configurable.V1_0.ConfigurableElementsType.ConfigurableElement> getConfigurableElement();
+  }
+
+  public static class ConfigurableElementsType.ConfigurableElement {
+    ctor public ConfigurableElementsType.ConfigurableElement();
+    method public String getPath();
+    method public void setPath(String);
+  }
+
+  public class ConfigurationFilePath {
+    ctor public ConfigurationFilePath();
+    method public String getPath();
+    method public void setPath(String);
+  }
+
+  public class ConfigurationsType {
+    ctor public ConfigurationsType();
+    method public java.util.List<audio.policy.configurable.V1_0.ConfigurationsType.Configuration> getConfiguration();
+  }
+
+  public static class ConfigurationsType.Configuration {
+    ctor public ConfigurationsType.Configuration();
+    method public audio.policy.configurable.V1_0.CompoundRuleType getCompoundRule();
+    method public String getName();
+    method public void setCompoundRule(audio.policy.configurable.V1_0.CompoundRuleType);
+    method public void setName(String);
+  }
+
+  public class EnumParameterType extends audio.policy.configurable.V1_0.Parameter {
+    ctor public EnumParameterType();
+    method public java.math.BigInteger getSize();
+    method public java.util.List<audio.policy.configurable.V1_0.EnumParameterType.ValuePair> getValuePair();
+    method public void setSize(java.math.BigInteger);
+  }
+
+  public static class EnumParameterType.ValuePair {
+    ctor public EnumParameterType.ValuePair();
+    method public String getLiteral();
+    method public String getNumerical();
+    method public void setLiteral(String);
+    method public void setNumerical(String);
+  }
+
+  public class FileIncluderType {
+    ctor public FileIncluderType();
+    method public String getPath();
+    method public void setPath(String);
+  }
+
+  public class FixedPointParameterType extends audio.policy.configurable.V1_0.PointParameterType {
+    ctor public FixedPointParameterType();
+    method public java.math.BigInteger getFractional();
+    method public java.math.BigInteger getIntegral();
+    method public java.math.BigInteger getSize();
+    method public void setFractional(java.math.BigInteger);
+    method public void setIntegral(java.math.BigInteger);
+    method public void setSize(java.math.BigInteger);
+  }
+
+  public class FloatingPointParameterType extends audio.policy.configurable.V1_0.PointParameterType {
+    ctor public FloatingPointParameterType();
+    method public String getMax();
+    method public String getMin();
+    method public java.math.BigInteger getSize();
+    method public void setMax(String);
+    method public void setMin(String);
+    method public void setSize(java.math.BigInteger);
+  }
+
+  public class IntegerParameterType extends audio.policy.configurable.V1_0.Parameter {
+    ctor public IntegerParameterType();
+    method public audio.policy.configurable.V1_0.LinearAdaptationType getLinearAdaptation();
+    method public audio.policy.configurable.V1_0.LogarithmicAdaptation getLogarithmicAdaptation();
+    method public java.math.BigInteger getMax();
+    method public java.math.BigInteger getMin();
+    method public boolean getSigned();
+    method public java.math.BigInteger getSize();
+    method public String getUnit();
+    method public void setLinearAdaptation(audio.policy.configurable.V1_0.LinearAdaptationType);
+    method public void setLogarithmicAdaptation(audio.policy.configurable.V1_0.LogarithmicAdaptation);
+    method public void setMax(java.math.BigInteger);
+    method public void setMin(java.math.BigInteger);
+    method public void setSigned(boolean);
+    method public void setSize(java.math.BigInteger);
+    method public void setUnit(String);
+  }
+
+  public enum LangEnum {
+    method public String getRawName();
+    enum_constant public static final audio.policy.configurable.V1_0.LangEnum EMPTY;
+  }
+
+  public class LinearAdaptationType extends audio.policy.configurable.V1_0.Adaptation {
+    ctor public LinearAdaptationType();
+    method public double getSlopeDenominator();
+    method public double getSlopeNumerator();
+    method public void setSlopeDenominator(double);
+    method public void setSlopeNumerator(double);
+  }
+
+  public class LogarithmicAdaptation extends audio.policy.configurable.V1_0.LinearAdaptationType {
+    ctor public LogarithmicAdaptation();
+    method public double getFloorValue();
+    method public double getLogarithmBase();
+    method public void setFloorValue(double);
+    method public void setLogarithmBase(double);
+  }
+
+  public enum MatchesWhenEnum {
+    method public String getRawName();
+    enum_constant public static final audio.policy.configurable.V1_0.MatchesWhenEnum Excludes;
+    enum_constant public static final audio.policy.configurable.V1_0.MatchesWhenEnum Includes;
+    enum_constant public static final audio.policy.configurable.V1_0.MatchesWhenEnum Is;
+    enum_constant public static final audio.policy.configurable.V1_0.MatchesWhenEnum IsNot;
+  }
+
+  public class Parameter {
+    ctor public Parameter();
+    method public java.math.BigInteger getArrayLength();
+    method public String getDescription();
+    method public String getMapping();
+    method public String getName();
+    method public void setArrayLength(java.math.BigInteger);
+    method public void setDescription(String);
+    method public void setMapping(String);
+    method public void setName(String);
+  }
+
+  public class ParameterBlockType {
+    ctor public ParameterBlockType();
+    method public java.math.BigInteger getArrayLength();
+    method public audio.policy.configurable.V1_0.BitParameterBlock getBitParameterBlock();
+    method public audio.policy.configurable.V1_0.BooleanParameter getBooleanParameter();
+    method public audio.policy.configurable.V1_0.ComponentInstance getComponent_optional();
+    method public String getDescription();
+    method public audio.policy.configurable.V1_0.EnumParameterType getEnumParameter();
+    method public audio.policy.configurable.V1_0.FixedPointParameterType getFixedPointParameter();
+    method public audio.policy.configurable.V1_0.FloatingPointParameterType getFloatingPointParameter();
+    method public audio.policy.configurable.V1_0.IntegerParameterType getIntegerParameter();
+    method public String getMapping();
+    method public String getName();
+    method public audio.policy.configurable.V1_0.ParameterBlockType getParameterBlock_optional();
+    method public audio.policy.configurable.V1_0.StringParameter getStringParameter();
+    method public void setArrayLength(java.math.BigInteger);
+    method public void setBitParameterBlock(audio.policy.configurable.V1_0.BitParameterBlock);
+    method public void setBooleanParameter(audio.policy.configurable.V1_0.BooleanParameter);
+    method public void setComponent_optional(audio.policy.configurable.V1_0.ComponentInstance);
+    method public void setDescription(String);
+    method public void setEnumParameter(audio.policy.configurable.V1_0.EnumParameterType);
+    method public void setFixedPointParameter(audio.policy.configurable.V1_0.FixedPointParameterType);
+    method public void setFloatingPointParameter(audio.policy.configurable.V1_0.FloatingPointParameterType);
+    method public void setIntegerParameter(audio.policy.configurable.V1_0.IntegerParameterType);
+    method public void setMapping(String);
+    method public void setName(String);
+    method public void setParameterBlock_optional(audio.policy.configurable.V1_0.ParameterBlockType);
+    method public void setStringParameter(audio.policy.configurable.V1_0.StringParameter);
+  }
+
+  public class ParameterFrameworkConfiguration {
+    ctor public ParameterFrameworkConfiguration();
+    method public String getServerPort();
+    method public audio.policy.configurable.V1_0.SettingsConfigurationType getSettingsConfiguration();
+    method public audio.policy.configurable.V1_0.ConfigurationFilePath getStructureDescriptionFileLocation();
+    method public audio.policy.configurable.V1_0.SubsystemPlugins getSubsystemPlugins();
+    method public String getSystemClassName();
+    method public boolean getTuningAllowed();
+    method public void setServerPort(String);
+    method public void setSettingsConfiguration(audio.policy.configurable.V1_0.SettingsConfigurationType);
+    method public void setStructureDescriptionFileLocation(audio.policy.configurable.V1_0.ConfigurationFilePath);
+    method public void setSubsystemPlugins(audio.policy.configurable.V1_0.SubsystemPlugins);
+    method public void setSystemClassName(String);
+    method public void setTuningAllowed(boolean);
+  }
+
+  public class ParameterType {
+    ctor public ParameterType();
+    method public String getName();
+    method public String getValue();
+    method public audio.policy.configurable.V1_0.ValueSpaceEnum getValueSpace();
+    method public void setName(String);
+    method public void setValue(String);
+    method public void setValueSpace(audio.policy.configurable.V1_0.ValueSpaceEnum);
+  }
+
+  public class PluginFile {
+    ctor public PluginFile();
+    method public String getName();
+    method public void setName(String);
+  }
+
+  public class PluginLocation {
+    ctor public PluginLocation();
+    method public String getFolder();
+    method public java.util.List<audio.policy.configurable.V1_0.PluginFile> getPlugin();
+    method public void setFolder(String);
+  }
+
+  public class PointParameterType extends audio.policy.configurable.V1_0.Parameter {
+    ctor public PointParameterType();
+    method public String getUnit();
+    method public void setUnit(String);
+  }
+
+  public class SelectionCriterionRuleType {
+    ctor public SelectionCriterionRuleType();
+    method public audio.policy.configurable.V1_0.MatchesWhenEnum getMatchesWhen();
+    method public String getSelectionCriterion();
+    method public String getValue();
+    method public void setMatchesWhen(audio.policy.configurable.V1_0.MatchesWhenEnum);
+    method public void setSelectionCriterion(String);
+    method public void setValue(String);
+  }
+
+  public class SettingsConfigurationType {
+    ctor public SettingsConfigurationType();
+    method public audio.policy.configurable.V1_0.ConfigurationFilePath getConfigurableDomainsFileLocation();
+    method public void setConfigurableDomainsFileLocation(audio.policy.configurable.V1_0.ConfigurationFilePath);
+  }
+
+  public class SettingsType {
+    ctor public SettingsType();
+    method public java.util.List<audio.policy.configurable.V1_0.SettingsType.Configuration> getConfiguration();
+  }
+
+  public static class SettingsType.Configuration {
+    ctor public SettingsType.Configuration();
+    method public java.util.List<audio.policy.configurable.V1_0.ConfigurableElementSettingsType> getConfigurableElement();
+    method public String getName();
+    method public void setName(String);
+  }
+
+  public enum SpaceEnum {
+    method public String getRawName();
+    enum_constant public static final audio.policy.configurable.V1_0.SpaceEnum _default;
+    enum_constant public static final audio.policy.configurable.V1_0.SpaceEnum preserve;
+  }
+
+  public class StringParameter {
+    ctor public StringParameter();
+    method public String getDescription();
+    method public String getMapping();
+    method public java.math.BigInteger getMaxLength();
+    method public String getName();
+    method public void setDescription(String);
+    method public void setMapping(String);
+    method public void setMaxLength(java.math.BigInteger);
+    method public void setName(String);
+  }
+
+  public class StringParameterType {
+    ctor public StringParameterType();
+    method public String getName();
+    method public String getValue();
+    method public void setName(String);
+    method public void setValue(String);
+  }
+
+  public class SubsystemPlugins {
+    ctor public SubsystemPlugins();
+    method public java.util.List<audio.policy.configurable.V1_0.PluginLocation> getLocation();
+  }
+
+  public class SubsystemType {
+    ctor public SubsystemType();
+    method public audio.policy.configurable.V1_0.ComponentTypeSetType getComponentLibrary();
+    method public String getDescription();
+    method public audio.policy.configurable.V1_0.SubsystemType.InstanceDefinition getInstanceDefinition();
+    method public String getMapping();
+    method public String getName();
+    method public String getType();
+    method public void setComponentLibrary(audio.policy.configurable.V1_0.ComponentTypeSetType);
+    method public void setDescription(String);
+    method public void setInstanceDefinition(audio.policy.configurable.V1_0.SubsystemType.InstanceDefinition);
+    method public void setMapping(String);
+    method public void setName(String);
+    method public void setType(String);
+  }
+
+  public static class SubsystemType.InstanceDefinition {
+    ctor public SubsystemType.InstanceDefinition();
+    method public audio.policy.configurable.V1_0.BitParameterBlock getBitParameterBlock();
+    method public audio.policy.configurable.V1_0.BooleanParameter getBooleanParameter();
+    method public audio.policy.configurable.V1_0.ComponentInstance getComponent_optional();
+    method public audio.policy.configurable.V1_0.EnumParameterType getEnumParameter();
+    method public audio.policy.configurable.V1_0.FixedPointParameterType getFixedPointParameter();
+    method public audio.policy.configurable.V1_0.FloatingPointParameterType getFloatingPointParameter();
+    method public audio.policy.configurable.V1_0.IntegerParameterType getIntegerParameter();
+    method public audio.policy.configurable.V1_0.ParameterBlockType getParameterBlock_optional();
+    method public audio.policy.configurable.V1_0.StringParameter getStringParameter();
+    method public void setBitParameterBlock(audio.policy.configurable.V1_0.BitParameterBlock);
+    method public void setBooleanParameter(audio.policy.configurable.V1_0.BooleanParameter);
+    method public void setComponent_optional(audio.policy.configurable.V1_0.ComponentInstance);
+    method public void setEnumParameter(audio.policy.configurable.V1_0.EnumParameterType);
+    method public void setFixedPointParameter(audio.policy.configurable.V1_0.FixedPointParameterType);
+    method public void setFloatingPointParameter(audio.policy.configurable.V1_0.FloatingPointParameterType);
+    method public void setIntegerParameter(audio.policy.configurable.V1_0.IntegerParameterType);
+    method public void setParameterBlock_optional(audio.policy.configurable.V1_0.ParameterBlockType);
+    method public void setStringParameter(audio.policy.configurable.V1_0.StringParameter);
+  }
+
+  public class SystemClass {
+    ctor public SystemClass();
+    method public String getName();
+    method public java.util.List<audio.policy.configurable.V1_0.SubsystemType> getSubsystem();
+    method public java.util.List<audio.policy.configurable.V1_0.FileIncluderType> getSubsystemInclude_optional();
+    method public void setName(String);
+  }
+
+  public enum TypeEnum {
+    method public String getRawName();
+    enum_constant public static final audio.policy.configurable.V1_0.TypeEnum All;
+    enum_constant public static final audio.policy.configurable.V1_0.TypeEnum Any;
+  }
+
+  public enum ValueSpaceEnum {
+    method public String getRawName();
+    enum_constant public static final audio.policy.configurable.V1_0.ValueSpaceEnum Raw;
+    enum_constant public static final audio.policy.configurable.V1_0.ValueSpaceEnum Real;
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method public static audio.policy.configurable.V1_0.BitParameterBlock readBitParameterBlock(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.BooleanParameter readBooleanParameter(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.ComponentTypeSetType readComponentTypeSetType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.ComponentTypeSetType readComponentTypeSetType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.ConfigurableDomainType readConfigurableDomainType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.ConfigurableDomains readConfigurableDomains(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.EnumParameterType readEnumParameterType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.FixedPointParameterType readFixedPointParameterType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.FloatingPointParameterType readFloatingPointParameterType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.IntegerParameterType readIntegerParameterType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.LinearAdaptationType readLinearAdaptationType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.LogarithmicAdaptation readLogarithmicAdaptation(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.ParameterFrameworkConfiguration readParameterFrameworkConfiguration(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.StringParameter readStringParameter(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.SubsystemPlugins readSubsystemPlugins(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.SubsystemType readSubsystemType(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.policy.configurable.V1_0.SystemClass readSystemClass(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+}
+
diff --git a/audio/policy/1.0/xml/pfw_schemas/api/last_current.txt b/audio/policy/1.0/xml/pfw_schemas/api/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/api/last_current.txt
diff --git a/audio/policy/1.0/xml/pfw_schemas/api/last_removed.txt b/audio/policy/1.0/xml/pfw_schemas/api/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/api/last_removed.txt
diff --git a/audio/policy/1.0/xml/pfw_schemas/api/removed.txt b/audio/policy/1.0/xml/pfw_schemas/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/audio/policy/1.0/xml/pfw_schemas/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/automotive/audiocontrol/2.0/Android.bp b/automotive/audiocontrol/2.0/Android.bp
new file mode 100644
index 0000000..2a9f849
--- /dev/null
+++ b/automotive/audiocontrol/2.0/Android.bp
@@ -0,0 +1,20 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.automotive.audiocontrol@2.0",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "types.hal",
+        "IAudioControl.hal",
+        "ICloseHandle.hal",
+        "IFocusListener.hal",
+    ],
+    interfaces: [
+        "android.hidl.base@1.0",
+        "android.hardware.audio.common@6.0",
+    ],
+    gen_java: true,
+}
diff --git a/automotive/audiocontrol/2.0/IAudioControl.hal b/automotive/audiocontrol/2.0/IAudioControl.hal
new file mode 100644
index 0000000..1073498
--- /dev/null
+++ b/automotive/audiocontrol/2.0/IAudioControl.hal
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.audiocontrol@2.0;
+
+import ICloseHandle;
+import IFocusListener;
+import android.hardware.audio.common@6.0::AudioUsage;
+
+/**
+ * Interacts with the car's audio subsystem to manage audio sources and volumes
+ */
+interface IAudioControl {
+    /**
+     * Registers focus listener to be used by HAL for requesting and abandoning audio focus.
+     *
+     * It is expected that there will only ever be a single focus listener registered. If the
+     * observer dies, the HAL implementation must unregister observer automatically. If called when
+     * a listener is already registered, the existing one should be unregistered and replaced with
+     * the new listener.
+     *
+     * @param listener the listener interface
+     * @return closeHandle A handle to unregister observer.
+     */
+    registerFocusListener(IFocusListener listener) generates (ICloseHandle closeHandle);
+
+    /**
+     * Notifies HAL of changes in audio focus status for focuses requested or abandoned by the HAL.
+     *
+     * This will be called in response to IFocusListener's requestAudioFocus and
+     * abandonAudioFocus, as well as part of any change in focus being held by the HAL due focus
+     * request from other activities or services.
+     *
+     * The HAL is not required to wait for an callback of AUDIOFOCUS_GAIN before playing audio, nor
+     * is it required to stop playing audio in the event of a AUDIOFOCUS_LOSS callback is received.
+     *
+     * @param usage The audio usage associated with the focus change {@code AttributeUsage}
+     * @param zoneId The identifier for the audio zone that the HAL is playing the stream in
+     * @param focusChange the AudioFocusChange that has occurred
+     */
+    oneway onAudioFocusChange(bitfield<AudioUsage> usage, int32_t zoneId,
+        bitfield<AudioFocusChange> focusChange);
+
+    /**
+     * Control the right/left balance setting of the car speakers.
+     *
+     * This is intended to shift the speaker volume toward the right (+) or left (-) side of
+     * the car. 0.0 means "centered". +1.0 means fully right. -1.0 means fully left.
+     *
+     * A value outside the range -1 to 1 must be clamped by the implementation to the -1 to 1
+     * range.
+     */
+    oneway setBalanceTowardRight(float value);
+
+    /**
+     * Control the fore/aft fade setting of the car speakers.
+     *
+     * This is intended to shift the speaker volume toward the front (+) or back (-) of the car.
+     * 0.0 means "centered". +1.0 means fully forward. -1.0 means fully rearward.
+     *
+     * A value outside the range -1 to 1 must be clamped by the implementation to the -1 to 1
+     * range.
+     */
+    oneway setFadeTowardFront(float value);
+};
diff --git a/automotive/audiocontrol/2.0/ICloseHandle.hal b/automotive/audiocontrol/2.0/ICloseHandle.hal
new file mode 100644
index 0000000..537af6d
--- /dev/null
+++ b/automotive/audiocontrol/2.0/ICloseHandle.hal
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.automotive.audiocontrol@2.0;
+
+/**
+ * Represents a generic close handle to remove a callback that doesn't need
+ * active interface.
+ *
+ * When close() is called OR when the interface is released, the underlying
+ * resources must be freed.
+ */
+interface ICloseHandle {
+    /**
+     * Closes the handle.
+     *
+     * The call must not fail and must be issued by the client at most once.
+     * Otherwise, the server must ignore subsequent calls.
+     */
+    close();
+};
diff --git a/automotive/audiocontrol/2.0/IFocusListener.hal b/automotive/audiocontrol/2.0/IFocusListener.hal
new file mode 100644
index 0000000..4fd5ef0
--- /dev/null
+++ b/automotive/audiocontrol/2.0/IFocusListener.hal
@@ -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.automotive.audiocontrol@2.0;
+
+import android.hardware.audio.common@6.0::AudioUsage;
+
+/**
+ * Callback interface for audio focus listener.
+ *
+ * For typical configuration, the listener the car audio service.
+ */
+interface IFocusListener {
+    /**
+     * Called whenever HAL is requesting focus as it is starting to play audio of a given usage in a
+     * specified zone.
+     *
+     * In response, IAudioControl#onAudioFocusChange will be called with focusChange status. This
+     * interaction is oneway to avoid blocking HAL so that it is not required to wait for a response
+     * before playing audio.
+     *
+     * @param usage The audio usage associated with the focus request {@code AttributeUsage}
+     * @param zoneId The identifier for the audio zone where the HAL is requesting focus
+     * @param focusGain The AudioFocusChange associated with this request. Should be one of the
+     * following: GAIN, GAIN_TRANSIENT, GAIN_TRANSIENT_MAY_DUCK, GAIN_TRANSIENT_EXCLUSIVE.
+     */
+    oneway requestAudioFocus(bitfield<AudioUsage> usage, int32_t zoneId,
+        bitfield<AudioFocusChange> focusGain);
+
+    /**
+     * Called whenever HAL is abandoning focus as it is finished playing audio of a given usage in a
+     * specific zone.
+     *
+     * In response, IAudioControl#onAudioFocusChange will be called with focusChange status. This
+     * interaction is oneway to avoid blocking HAL so that it is not required to wait for a response
+     * before stopping audio playback.
+     *
+     * @param usage The audio usage for which the HAL is abandoning focus {@code AttributeUsage}
+     * @param zoneId The identifier for the audio zone that the HAL abandoning focus
+     */
+    oneway abandonAudioFocus(bitfield<AudioUsage> usage, int32_t zoneId);
+};
diff --git a/automotive/audiocontrol/2.0/default/Android.bp b/automotive/audiocontrol/2.0/default/Android.bp
new file mode 100644
index 0000000..44ad028
--- /dev/null
+++ b/automotive/audiocontrol/2.0/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.
+
+cc_binary {
+    name: "android.hardware.automotive.audiocontrol@2.0-service",
+    defaults: ["hidl_defaults"],
+    vendor: true,
+    relative_install_path: "hw",
+    srcs: [
+        "AudioControl.cpp",
+        "service.cpp",
+        "CloseHandle.cpp",
+    ],
+    init_rc: ["android.hardware.automotive.audiocontrol@2.0-service.rc"],
+
+    shared_libs: [
+        "android.hardware.automotive.audiocontrol@2.0",
+        "libbase",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+    vintf_fragments: ["audiocontrol2_manifest.xml"],
+    cflags: [
+        "-O0",
+        "-g",
+    ],
+}
diff --git a/automotive/audiocontrol/2.0/default/AudioControl.cpp b/automotive/audiocontrol/2.0/default/AudioControl.cpp
new file mode 100644
index 0000000..6505e34
--- /dev/null
+++ b/automotive/audiocontrol/2.0/default/AudioControl.cpp
@@ -0,0 +1,60 @@
+#include "AudioControl.h"
+
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "CloseHandle.h"
+
+namespace android::hardware::automotive::audiocontrol::V2_0::implementation {
+
+AudioControl::AudioControl() {}
+
+Return<sp<ICloseHandle>> AudioControl::registerFocusListener(const sp<IFocusListener>& listener) {
+    LOG(DEBUG) << "registering focus listener";
+    sp<ICloseHandle> closeHandle(nullptr);
+
+    if (listener) {
+        mFocusListener = listener;
+
+        closeHandle = new CloseHandle([this, listener]() {
+            if (mFocusListener == listener) {
+                mFocusListener = nullptr;
+            }
+        });
+    } else {
+        LOG(ERROR) << "Unexpected nullptr for listener resulting in no-op.";
+    }
+
+    return closeHandle;
+}
+
+Return<void> AudioControl::setBalanceTowardRight(float value) {
+    // For completeness, lets bounds check the input...
+    if (isValidValue(value)) {
+        LOG(ERROR) << "Balance value out of range -1 to 1 at " << value;
+    } else {
+        // Just log in this default mock implementation
+        LOG(INFO) << "Balance set to " << value;
+    }
+    return Void();
+}
+
+Return<void> AudioControl::setFadeTowardFront(float value) {
+    // For completeness, lets bounds check the input...
+    if (isValidValue(value)) {
+        LOG(ERROR) << "Fader value out of range -1 to 1 at " << value;
+    } else {
+        // Just log in this default mock implementation
+        LOG(INFO) << "Fader set to " << value;
+    }
+    return Void();
+}
+
+Return<void> AudioControl::onAudioFocusChange(hidl_bitfield<AudioUsage> usage, int zoneId,
+                                              hidl_bitfield<AudioFocusChange> focusChange) {
+    LOG(INFO) << "Focus changed: " << static_cast<int>(focusChange) << " for usage "
+              << static_cast<int>(usage) << " in zone " << zoneId;
+    return Void();
+}
+
+}  // namespace android::hardware::automotive::audiocontrol::V2_0::implementation
diff --git a/automotive/audiocontrol/2.0/default/AudioControl.h b/automotive/audiocontrol/2.0/default/AudioControl.h
new file mode 100644
index 0000000..475a693
--- /dev/null
+++ b/automotive/audiocontrol/2.0/default/AudioControl.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_AUTOMOTIVE_AUDIOCONTROL_V2_0_AUDIOCONTROL_H
+#define ANDROID_HARDWARE_AUTOMOTIVE_AUDIOCONTROL_V2_0_AUDIOCONTROL_H
+
+#include <android/hardware/audio/common/6.0/types.h>
+#include <android/hardware/automotive/audiocontrol/2.0/IAudioControl.h>
+#include <android/hardware/automotive/audiocontrol/2.0/ICloseHandle.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+using android::hardware::audio::common::V6_0::AudioUsage;
+
+namespace android::hardware::automotive::audiocontrol::V2_0::implementation {
+
+class AudioControl : public IAudioControl {
+  public:
+    // Methods from ::android::hardware::automotive::audiocontrol::V2_0::IAudioControl follow.
+    Return<sp<ICloseHandle>> registerFocusListener(const sp<IFocusListener>& listener);
+    Return<void> onAudioFocusChange(hidl_bitfield<AudioUsage> usage, int zoneId,
+                                    hidl_bitfield<AudioFocusChange> focusChange);
+    Return<void> setBalanceTowardRight(float value) override;
+    Return<void> setFadeTowardFront(float value) override;
+
+    // Implementation details
+    AudioControl();
+
+  private:
+    sp<IFocusListener> mFocusListener;
+    static bool isValidValue(float value) { return (value > 1.0f) || (value < -1.0f); }
+};
+
+}  // namespace android::hardware::automotive::audiocontrol::V2_0::implementation
+
+#endif  // ANDROID_HARDWARE_AUTOMOTIVE_AUDIOCONTROL_V2_0_AUDIOCONTROL_H
diff --git a/automotive/audiocontrol/2.0/default/CloseHandle.cpp b/automotive/audiocontrol/2.0/default/CloseHandle.cpp
new file mode 100644
index 0000000..bc47931
--- /dev/null
+++ b/automotive/audiocontrol/2.0/default/CloseHandle.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CloseHandle.h"
+
+namespace android::hardware::automotive::audiocontrol::V2_0::implementation {
+
+CloseHandle::CloseHandle(Callback callback) : mCallback(callback) {}
+
+CloseHandle::~CloseHandle() {
+    close();
+}
+
+Return<void> CloseHandle::close() {
+    const auto wasClosed = mIsClosed.exchange(true);
+    if (wasClosed) return {};
+
+    if (mCallback) mCallback();
+    return {};
+}
+
+}  // namespace android::hardware::automotive::audiocontrol::V2_0::implementation
diff --git a/automotive/audiocontrol/2.0/default/CloseHandle.h b/automotive/audiocontrol/2.0/default/CloseHandle.h
new file mode 100644
index 0000000..6caf0bf
--- /dev/null
+++ b/automotive/audiocontrol/2.0/default/CloseHandle.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/macros.h>
+#include <android/hardware/automotive/audiocontrol/2.0/ICloseHandle.h>
+
+namespace android::hardware::automotive::audiocontrol::V2_0::implementation {
+
+/** Generic ICloseHandle implementation ignoring double-close events. */
+class CloseHandle : public ICloseHandle {
+  public:
+    using Callback = std::function<void()>;
+
+    /**
+     * Create a handle with a callback.
+     *
+     * The callback is guaranteed to be called exactly once.
+     *
+     * \param callback Called on the first close() call, or on destruction of the handle
+     */
+    CloseHandle(Callback callback = nullptr);
+    virtual ~CloseHandle();
+
+    Return<void> close() override;
+
+  private:
+    const Callback mCallback;
+    std::atomic<bool> mIsClosed = false;
+
+    DISALLOW_COPY_AND_ASSIGN(CloseHandle);
+};
+
+}  // namespace android::hardware::automotive::audiocontrol::V2_0::implementation
diff --git a/automotive/audiocontrol/2.0/default/android.hardware.automotive.audiocontrol@2.0-service.rc b/automotive/audiocontrol/2.0/default/android.hardware.automotive.audiocontrol@2.0-service.rc
new file mode 100644
index 0000000..81c9be4
--- /dev/null
+++ b/automotive/audiocontrol/2.0/default/android.hardware.automotive.audiocontrol@2.0-service.rc
@@ -0,0 +1,4 @@
+service vendor.audiocontrol-hal-2.0 /vendor/bin/hw/android.hardware.automotive.audiocontrol@2.0-service
+    class hal
+    user audioserver
+    group system
diff --git a/automotive/audiocontrol/2.0/default/audiocontrol2_manifest.xml b/automotive/audiocontrol/2.0/default/audiocontrol2_manifest.xml
new file mode 100644
index 0000000..42d23ed
--- /dev/null
+++ b/automotive/audiocontrol/2.0/default/audiocontrol2_manifest.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.automotive.audiocontrol</name>
+        <transport>hwbinder</transport>
+        <version>2.0</version>
+        <interface>
+            <name>IAudioControl</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
\ No newline at end of file
diff --git a/automotive/audiocontrol/2.0/default/service.cpp b/automotive/audiocontrol/2.0/default/service.cpp
new file mode 100644
index 0000000..dcc46c3
--- /dev/null
+++ b/automotive/audiocontrol/2.0/default/service.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include "AudioControl.h"
+
+// libhidl:
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+// Generated HIDL files
+using android::hardware::automotive::audiocontrol::V2_0::IAudioControl;
+
+// The namespace in which all our implementation code lives
+using namespace android::hardware::automotive::audiocontrol::V2_0::implementation;
+using namespace android;
+
+// Main service entry point
+int main() {
+    // Create an instance of our service class
+    android::sp<IAudioControl> service = new AudioControl();
+    configureRpcThreadpool(1, true /*callerWillJoin*/);
+
+    if (service->registerAsService() != OK) {
+        LOG(ERROR) << "registerAsService failed";
+        return 1;
+    }
+
+    // Join (forever) the thread pool we created for the service above
+    joinRpcThreadpool();
+
+    // We don't ever actually expect to return, so return an error if we do get here
+    return 2;
+}
\ No newline at end of file
diff --git a/automotive/audiocontrol/2.0/types.hal b/automotive/audiocontrol/2.0/types.hal
new file mode 100644
index 0000000..65b0988
--- /dev/null
+++ b/automotive/audiocontrol/2.0/types.hal
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.audiocontrol@2.0;
+
+/**
+ * Changes in audio focus that can be experienced
+ */
+enum AudioFocusChange : uint32_t {
+    NONE = 0,
+    GAIN = 1,
+    GAIN_TRANSIENT = 2,
+    GAIN_TRANSIENT_MAY_DUCK = 3,
+    GAIN_TRANSIENT_EXCLUSIVE = 4,
+    LOSS = -1 * GAIN,
+    LOSS_TRANSIENT = -1 * GAIN_TRANSIENT,
+    LOSS_TRANSIENT_CAN_DUCK = -1 * GAIN_TRANSIENT_MAY_DUCK,
+};
diff --git a/automotive/audiocontrol/2.0/vts/functional/Android.bp b/automotive/audiocontrol/2.0/vts/functional/Android.bp
new file mode 100644
index 0000000..520b042
--- /dev/null
+++ b/automotive/audiocontrol/2.0/vts/functional/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "VtsHalAudioControlV2_0TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalAudioControlV2_0TargetTest.cpp"],
+    static_libs: [
+        "android.hardware.automotive.audiocontrol@2.0",
+        "libgmock",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts-core",
+    ],
+}
diff --git a/automotive/audiocontrol/2.0/vts/functional/VtsHalAudioControlV2_0TargetTest.cpp b/automotive/audiocontrol/2.0/vts/functional/VtsHalAudioControlV2_0TargetTest.cpp
new file mode 100644
index 0000000..0c10664
--- /dev/null
+++ b/automotive/audiocontrol/2.0/vts/functional/VtsHalAudioControlV2_0TargetTest.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "VtsHalAudioControlTest"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <hidl/HidlTransportSupport.h>
+#include <hwbinder/ProcessState.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include <android/hardware/audio/common/6.0/types.h>
+#include <android/hardware/automotive/audiocontrol/2.0/IAudioControl.h>
+#include <android/hardware/automotive/audiocontrol/2.0/types.h>
+#include <android/log.h>
+
+using namespace ::android::hardware::automotive::audiocontrol::V2_0;
+using ::android::sp;
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_enum_range;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::audio::common::V6_0::AudioUsage;
+
+// The main test class for the automotive AudioControl HAL
+class CarAudioControlHidlTest : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        // Make sure we can connect to the driver
+        pAudioControl = IAudioControl::getService(GetParam());
+        ASSERT_NE(pAudioControl.get(), nullptr);
+    }
+
+    virtual void TearDown() override {}
+
+  protected:
+    sp<IAudioControl> pAudioControl;  // Every test needs access to the service
+};
+
+//
+// Tests start here...
+//
+
+/*
+ * Fader exercise test.  Note that only a subjective observer could determine if the
+ * fader actually works.  The only thing we can do is exercise the HAL and if the HAL crashes,
+ * we _might_ get a test failure if that breaks the connection to the driver.
+ */
+TEST_P(CarAudioControlHidlTest, FaderExercise) {
+    ALOGI("Fader exercise test (silent)");
+
+    // Set the fader all the way to the back
+    pAudioControl->setFadeTowardFront(-1.0f);
+
+    // Set the fader all the way to the front
+    pAudioControl->setFadeTowardFront(1.0f);
+
+    // Set the fader part way toward the back
+    pAudioControl->setFadeTowardFront(-0.333f);
+
+    // Set the fader to a out of bounds value (driver should clamp)
+    pAudioControl->setFadeTowardFront(99999.9f);
+
+    // Set the fader back to the middle
+    pAudioControl->setFadeTowardFront(0.0f);
+}
+
+/*
+ * Balance exercise test.
+ */
+TEST_P(CarAudioControlHidlTest, BalanceExercise) {
+    ALOGI("Balance exercise test (silent)");
+
+    // Set the balance all the way to the left
+    pAudioControl->setBalanceTowardRight(-1.0f);
+
+    // Set the balance all the way to the right
+    pAudioControl->setBalanceTowardRight(1.0f);
+
+    // Set the balance part way toward the left
+    pAudioControl->setBalanceTowardRight(-0.333f);
+
+    // Set the balance to a out of bounds value (driver should clamp)
+    pAudioControl->setBalanceTowardRight(99999.9f);
+
+    // Set the balance back to the middle
+    pAudioControl->setBalanceTowardRight(0.0f);
+}
+
+struct FocusListenerMock : public IFocusListener {
+    MOCK_METHOD(Return<void>, requestAudioFocus,
+                (hidl_bitfield<AudioUsage> usage, int zoneId,
+                 hidl_bitfield<AudioFocusChange> focusGain));
+    MOCK_METHOD(Return<void>, abandonAudioFocus, (hidl_bitfield<AudioUsage> usage, int zoneId));
+};
+
+/*
+ * Test focus listener registration.
+ *
+ * Verifies that:
+ * - registerFocusListener succeeds;
+ * - registering a second listener succeeds in replacing the first;
+ * - closing handle does not crash;
+ */
+TEST_P(CarAudioControlHidlTest, FocusListenerRegistration) {
+    ALOGI("Focus listener test");
+
+    sp<FocusListenerMock> listener = new FocusListenerMock();
+
+    auto hidlResult = pAudioControl->registerFocusListener(listener);
+    ASSERT_TRUE(hidlResult.isOk());
+
+    sp<FocusListenerMock> listener2 = new FocusListenerMock();
+
+    auto hidlResult2 = pAudioControl->registerFocusListener(listener2);
+    ASSERT_TRUE(hidlResult2.isOk());
+
+    const sp<ICloseHandle>& closeHandle = hidlResult2;
+    closeHandle->close();
+};
+
+TEST_P(CarAudioControlHidlTest, FocusChangeExercise) {
+    ALOGI("Focus Change test");
+
+    pAudioControl->onAudioFocusChange(AudioUsage::MEDIA | 0, 0,
+                                      AudioFocusChange::GAIN_TRANSIENT | 0);
+};
+
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, CarAudioControlHidlTest,
+        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IAudioControl::descriptor)),
+        android::hardware::PrintInstanceNameToString);
\ No newline at end of file
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 2050038..a94a37e 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -82,6 +82,20 @@
     ],
 }
 
+// VHal virtualization utils
+cc_library_static {
+    name: "android.hardware.automotive.vehicle@2.0-virtualization-utils",
+    vendor: true,
+    defaults: ["vhal_v2_0_defaults"],
+    srcs: [
+        "impl/vhal_v2_0/virtualization/Utils.cpp",
+    ],
+    export_include_dirs: ["impl"],
+    shared_libs: [
+        "libbase",
+    ],
+}
+
 cc_test {
     name: "android.hardware.automotive.vehicle@2.0-manager-unit-tests",
     vendor: true,
@@ -133,3 +147,59 @@
         "libqemu_pipe",
     ],
 }
+
+cc_binary {
+    name: "android.hardware.automotive.vehicle@2.0-virtualization-service",
+    defaults: ["vhal_v2_0_defaults"],
+    init_rc: ["android.hardware.automotive.vehicle@2.0-virtualization-service.rc"],
+    vendor: true,
+    relative_install_path: "hw",
+    srcs: [
+        "impl/vhal_v2_0/virtualization/GrpcVehicleClient.cpp",
+        "VirtualizedVehicleService.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libjsoncpp",
+        "libprotobuf-cpp-full",
+        "libgrpc++",
+    ],
+    static_libs: [
+        "android.hardware.automotive.vehicle@2.0-manager-lib",
+        "android.hardware.automotive.vehicle@2.0-default-impl-lib",
+        "android.hardware.automotive.vehicle@2.0-grpc",
+        "android.hardware.automotive.vehicle@2.0-virtualization-utils",
+        "libqemu_pipe",
+    ],
+    cflags: [
+        "-Wno-unused-parameter"
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.automotive.vehicle@2.0-virtualization-grpc-server",
+    init_rc: ["android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc"],
+    defaults: ["vhal_v2_0_defaults"],
+    vendor: true,
+    relative_install_path: "hw",
+    srcs: [
+        "impl/vhal_v2_0/virtualization/GrpcVehicleServer.cpp",
+        "VirtualizationGrpcServer.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libjsoncpp",
+        "libprotobuf-cpp-full",
+        "libgrpc++",
+    ],
+    static_libs: [
+        "android.hardware.automotive.vehicle@2.0-manager-lib",
+        "android.hardware.automotive.vehicle@2.0-default-impl-lib",
+        "android.hardware.automotive.vehicle@2.0-grpc",
+        "android.hardware.automotive.vehicle@2.0-virtualization-utils",
+    ],
+    cflags: [
+        "-Wno-unused-parameter"
+    ],
+}
diff --git a/automotive/vehicle/2.0/default/VirtualizationGrpcServer.cpp b/automotive/vehicle/2.0/default/VirtualizationGrpcServer.cpp
new file mode 100644
index 0000000..cca65d9
--- /dev/null
+++ b/automotive/vehicle/2.0/default/VirtualizationGrpcServer.cpp
@@ -0,0 +1,49 @@
+#include <android-base/logging.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include "vhal_v2_0/virtualization/GrpcVehicleServer.h"
+#include "vhal_v2_0/virtualization/Utils.h"
+
+int main(int argc, char* argv[]) {
+    namespace vhal_impl = android::hardware::automotive::vehicle::V2_0::impl;
+
+    vhal_impl::VsockServerInfo serverInfo;
+
+    // unique values to identify the options
+    constexpr int OPT_VHAL_SERVER_CID = 1001;
+    constexpr int OPT_VHAL_SERVER_PORT_NUMBER = 1002;
+
+    struct option longOptions[] = {
+            {"server_cid", 1, 0, OPT_VHAL_SERVER_CID},
+            {"server_port", 1, 0, OPT_VHAL_SERVER_PORT_NUMBER},
+            {nullptr, 0, nullptr, 0},
+    };
+
+    int optValue;
+    while ((optValue = getopt_long_only(argc, argv, ":", longOptions, 0)) != -1) {
+        switch (optValue) {
+            case OPT_VHAL_SERVER_CID:
+                serverInfo.serverCid = std::atoi(optarg);
+                LOG(DEBUG) << "Vehicle HAL server CID: " << serverInfo.serverCid;
+                break;
+            case OPT_VHAL_SERVER_PORT_NUMBER:
+                serverInfo.serverPort = std::atoi(optarg);
+                LOG(DEBUG) << "Vehicle HAL server port: " << serverInfo.serverPort;
+                break;
+            default:
+                // ignore other options
+                break;
+        }
+    }
+
+    if (serverInfo.serverCid == 0 || serverInfo.serverPort == 0) {
+        LOG(FATAL) << "Invalid server information, CID: " << serverInfo.serverCid
+                   << "; port: " << serverInfo.serverPort;
+        // Will abort after logging
+    }
+
+    auto server = vhal_impl::makeGrpcVehicleServer(vhal_impl::getVsockUri(serverInfo));
+    server->Start();
+    return 0;
+}
diff --git a/automotive/vehicle/2.0/default/VirtualizedVehicleService.cpp b/automotive/vehicle/2.0/default/VirtualizedVehicleService.cpp
new file mode 100644
index 0000000..1de81ae
--- /dev/null
+++ b/automotive/vehicle/2.0/default/VirtualizedVehicleService.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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 <android-base/logging.h>
+#include <cutils/properties.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include <vhal_v2_0/EmulatedVehicleConnector.h>
+#include <vhal_v2_0/EmulatedVehicleHal.h>
+#include <vhal_v2_0/VehicleHalManager.h>
+#include <vhal_v2_0/virtualization/GrpcVehicleClient.h>
+#include <vhal_v2_0/virtualization/Utils.h>
+
+using namespace android;
+using namespace android::hardware;
+using namespace android::hardware::automotive::vehicle::V2_0;
+
+int main(int argc, char* argv[]) {
+    constexpr const char* VHAL_SERVER_CID_PROPERTY_KEY = "ro.vendor.vehiclehal.server.cid";
+    constexpr const char* VHAL_SERVER_PORT_PROPERTY_KEY = "ro.vendor.vehiclehal.server.port";
+
+    auto property_get_uint = [](const char* key, unsigned int default_value) {
+        auto value = property_get_int64(key, default_value);
+        if (value < 0 || value > UINT_MAX) {
+            LOG(DEBUG) << key << ": " << value << " is out of bound, using default value '"
+                       << default_value << "' instead";
+            return default_value;
+        }
+        return static_cast<unsigned int>(value);
+    };
+
+    impl::VsockServerInfo serverInfo{property_get_uint(VHAL_SERVER_CID_PROPERTY_KEY, 0),
+                                     property_get_uint(VHAL_SERVER_PORT_PROPERTY_KEY, 0)};
+
+    if (serverInfo.serverCid == 0 || serverInfo.serverPort == 0) {
+        LOG(FATAL) << "Invalid server information, CID: " << serverInfo.serverCid
+                   << "; port: " << serverInfo.serverPort;
+        // Will abort after logging
+    }
+
+    auto store = std::make_unique<VehiclePropertyStore>();
+    auto connector = impl::makeGrpcVehicleClient(impl::getVsockUri(serverInfo));
+    auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get());
+    auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get());
+    auto service = std::make_unique<VehicleHalManager>(hal.get());
+
+    configureRpcThreadpool(4, true /* callerWillJoin */);
+
+    LOG(INFO) << "Registering as service...";
+    status_t status = service->registerAsService();
+
+    if (status != OK) {
+        LOG(ERROR) << "Unable to register vehicle service (" << status << ")";
+        return 1;
+    }
+
+    LOG(INFO) << "Ready";
+    joinRpcThreadpool();
+
+    return 1;
+}
diff --git a/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc
new file mode 100644
index 0000000..29147ad
--- /dev/null
+++ b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc
@@ -0,0 +1,10 @@
+# It is an interim state to run GRPC server as an Android service.
+# Eventually it will run outside of Android (e.g., AGL),
+# so the command line arguments are expected, though not conventionally used in Android
+service vendor.vehicle-hal-2.0-server \
+        /vendor/bin/hw/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server \
+        -server_cid ${ro.vendor.vehiclehal.server.cid:-0} \
+        -server_port ${ro.vendor.vehiclehal.server.port:-0}
+    class hal
+    user vehicle_network
+    group system inet
diff --git a/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-service.rc b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-service.rc
new file mode 100644
index 0000000..234de59
--- /dev/null
+++ b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-service.rc
@@ -0,0 +1,4 @@
+service vendor.vehicle-hal-2.0 /vendor/bin/hw/android.hardware.automotive.vehicle@2.0-virtualization-service
+    class hal
+    user vehicle_network
+    group system inet
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 2dbcbba..bf85da2 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
@@ -255,6 +255,13 @@
          .initialValue = {.stringValue = "Toy Vehicle"}},
         {.config =
                  {
+                         .prop = toInt(VehicleProperty::INFO_EXTERIOR_DIMENSIONS),
+                         .access = VehiclePropertyAccess::READ,
+                         .changeMode = VehiclePropertyChangeMode::STATIC,
+                 },
+         .initialValue = {.floatValues = {1776, 4950, 2008, 2140, 2984, 1665, 1667, 11800}}},
+        {.config =
+                 {
                          .prop = toInt(VehicleProperty::PERF_VEHICLE_SPEED),
                          .access = VehiclePropertyAccess::READ,
                          .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
@@ -293,7 +300,24 @@
                          .maxSampleRate = 10.0f,
                  },
          .initialValue = {.floatValues = {0.0f}}},
-
+        {.config =
+                 {
+                         .prop = toInt(VehicleProperty::PERF_STEERING_ANGLE),
+                         .access = VehiclePropertyAccess::READ,
+                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+                         .minSampleRate = 0.0f,
+                         .maxSampleRate = 10.0f,
+                 },
+         .initialValue = {.floatValues = {0.0f}}},
+        {.config =
+                 {
+                         .prop = toInt(VehicleProperty::PERF_REAR_STEERING_ANGLE),
+                         .access = VehiclePropertyAccess::READ,
+                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+                         .minSampleRate = 0.0f,
+                         .maxSampleRate = 10.0f,
+                 },
+         .initialValue = {.floatValues = {0.0f}}},
         {
                 .config =
                         {
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleServer.proto b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleServer.proto
index 2cc6595..6f71d65 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleServer.proto
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleServer.proto
@@ -35,13 +35,23 @@
     VehicleHalStatusCode status_code    = 1;
 }
 
+message WrappedVehiclePropValue {
+    VehiclePropValue value              = 1;
+    // An indicator on whether we should update the status of the property
+    //   - true: if the value is generated by (emulated/real) car, or;
+    //           if the value is injected to 'fake' a on car event (for debugging purpose)
+    //   - false: if the value is set by VHal (public interface), since Android
+    //            cannot change status of property on a real car
+    bool update_status                  = 2;
+}
+
 service VehicleServer {
     rpc GetAllPropertyConfig(google.protobuf.Empty) returns (stream VehiclePropConfig) {}
 
     // Change the property value of the vehicle
-    rpc SetProperty(VehiclePropValue) returns (VehicleHalCallStatus) {}
+    rpc SetProperty(WrappedVehiclePropValue) returns (VehicleHalCallStatus) {}
 
     // Start a vehicle property value stream
-    rpc StartPropertyValuesStream(google.protobuf.Empty) returns (stream VehiclePropValue) {}
+    rpc StartPropertyValuesStream(google.protobuf.Empty) returns (stream WrappedVehiclePropValue) {}
 }
 
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.cpp
new file mode 100644
index 0000000..e329c5b
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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 "GrpcVehicleClient.h"
+
+#include <condition_variable>
+#include <mutex>
+
+#include <android-base/logging.h>
+#include <grpc++/grpc++.h>
+
+#include "VehicleServer.grpc.pb.h"
+#include "VehicleServer.pb.h"
+#include "vhal_v2_0/ProtoMessageConverter.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+static std::shared_ptr<::grpc::ChannelCredentials> getChannelCredentials() {
+    // TODO(chenhaosjtuacm): get secured credentials here
+    return ::grpc::InsecureChannelCredentials();
+}
+
+class GrpcVehicleClientImpl : public EmulatedVehicleClient {
+  public:
+    GrpcVehicleClientImpl(const std::string& addr)
+        : mServiceAddr(addr),
+          mGrpcChannel(::grpc::CreateChannel(mServiceAddr, getChannelCredentials())),
+          mGrpcStub(vhal_proto::VehicleServer::NewStub(mGrpcChannel)) {
+        StartValuePollingThread();
+    }
+
+    ~GrpcVehicleClientImpl() {
+        mShuttingDownFlag.store(true);
+        mShutdownCV.notify_all();
+        if (mPollingThread.joinable()) {
+            mPollingThread.join();
+        }
+    }
+
+    // methods from IVehicleClient
+
+    std::vector<VehiclePropConfig> getAllPropertyConfig() const override;
+
+    StatusCode setProperty(const VehiclePropValue& value, bool updateStatus) override;
+
+  private:
+    void StartValuePollingThread();
+
+    // private data members
+
+    std::string mServiceAddr;
+    std::shared_ptr<::grpc::Channel> mGrpcChannel;
+    std::unique_ptr<vhal_proto::VehicleServer::Stub> mGrpcStub;
+    std::thread mPollingThread;
+
+    std::mutex mShutdownMutex;
+    std::condition_variable mShutdownCV;
+    std::atomic<bool> mShuttingDownFlag{false};
+};
+
+std::unique_ptr<EmulatedVehicleClient> makeGrpcVehicleClient(const std::string& addr) {
+    return std::make_unique<GrpcVehicleClientImpl>(addr);
+}
+
+std::vector<VehiclePropConfig> GrpcVehicleClientImpl::getAllPropertyConfig() const {
+    std::vector<VehiclePropConfig> configs;
+    ::grpc::ClientContext context;
+    auto config_stream = mGrpcStub->GetAllPropertyConfig(&context, ::google::protobuf::Empty());
+    vhal_proto::VehiclePropConfig protoConfig;
+    while (config_stream->Read(&protoConfig)) {
+        VehiclePropConfig config;
+        proto_msg_converter::fromProto(&config, protoConfig);
+        configs.emplace_back(std::move(config));
+    }
+    auto grpc_status = config_stream->Finish();
+    if (!grpc_status.ok()) {
+        LOG(ERROR) << __func__
+                   << ": GRPC GetAllPropertyConfig Failed: " << grpc_status.error_message();
+        configs.clear();
+    }
+
+    return configs;
+}
+
+StatusCode GrpcVehicleClientImpl::setProperty(const VehiclePropValue& value, bool updateStatus) {
+    ::grpc::ClientContext context;
+    vhal_proto::WrappedVehiclePropValue wrappedProtoValue;
+    vhal_proto::VehicleHalCallStatus vhal_status;
+    proto_msg_converter::toProto(wrappedProtoValue.mutable_value(), value);
+    wrappedProtoValue.set_update_status(updateStatus);
+
+    auto grpc_status = mGrpcStub->SetProperty(&context, wrappedProtoValue, &vhal_status);
+    if (!grpc_status.ok()) {
+        LOG(ERROR) << __func__ << ": GRPC SetProperty Failed: " << grpc_status.error_message();
+        return StatusCode::INTERNAL_ERROR;
+    }
+
+    return static_cast<StatusCode>(vhal_status.status_code());
+}
+
+void GrpcVehicleClientImpl::StartValuePollingThread() {
+    mPollingThread = std::thread([this]() {
+        while (!mShuttingDownFlag.load()) {
+            ::grpc::ClientContext context;
+
+            std::atomic<bool> rpc_ok{true};
+            std::thread shuttingdown_watcher([this, &rpc_ok, &context]() {
+                std::unique_lock<std::mutex> shutdownLock(mShutdownMutex);
+                mShutdownCV.wait(shutdownLock, [this, &rpc_ok]() {
+                    return !rpc_ok.load() || mShuttingDownFlag.load();
+                });
+                context.TryCancel();
+            });
+
+            auto value_stream =
+                    mGrpcStub->StartPropertyValuesStream(&context, ::google::protobuf::Empty());
+            vhal_proto::WrappedVehiclePropValue wrappedProtoValue;
+            while (!mShuttingDownFlag.load() && value_stream->Read(&wrappedProtoValue)) {
+                VehiclePropValue value;
+                proto_msg_converter::fromProto(&value, wrappedProtoValue.value());
+                onPropertyValue(value, wrappedProtoValue.update_status());
+            }
+
+            rpc_ok.store(false);
+            mShutdownCV.notify_all();
+            shuttingdown_watcher.join();
+
+            auto grpc_status = value_stream->Finish();
+            // never reach here until connection lost
+            LOG(ERROR) << __func__
+                       << ": GRPC Value Streaming Failed: " << grpc_status.error_message();
+
+            // try to reconnect
+        }
+    });
+}
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.h
new file mode 100644
index 0000000..14eae7f
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.h
@@ -0,0 +1,40 @@
+/*
+ * 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_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleClient_H_
+#define android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleClient_H_
+
+#include "vhal_v2_0/EmulatedVehicleConnector.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+std::unique_ptr<EmulatedVehicleClient> makeGrpcVehicleClient(const std::string& addr);
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleClient_H_
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.cpp
new file mode 100644
index 0000000..e30b3be
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.cpp
@@ -0,0 +1,229 @@
+/*
+ * 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 "GrpcVehicleServer.h"
+
+#include <condition_variable>
+#include <mutex>
+#include <shared_mutex>
+
+#include <android-base/logging.h>
+#include <grpc++/grpc++.h>
+
+#include "VehicleServer.grpc.pb.h"
+#include "VehicleServer.pb.h"
+#include "vhal_v2_0/ProtoMessageConverter.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+class GrpcVehicleServerImpl : public GrpcVehicleServer, public vhal_proto::VehicleServer::Service {
+  public:
+    GrpcVehicleServerImpl(const std::string& addr) : mServiceAddr(addr) {
+        setValuePool(&mValueObjectPool);
+    }
+
+    // method from GrpcVehicleServer
+    void Start() override;
+
+    // method from IVehicleServer
+    void onPropertyValueFromCar(const VehiclePropValue& value, bool updateStatus) override;
+
+    // methods from vhal_proto::VehicleServer::Service
+
+    ::grpc::Status GetAllPropertyConfig(
+            ::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+            ::grpc::ServerWriter<vhal_proto::VehiclePropConfig>* stream) override;
+
+    ::grpc::Status SetProperty(::grpc::ServerContext* context,
+                               const vhal_proto::WrappedVehiclePropValue* wrappedPropValue,
+                               vhal_proto::VehicleHalCallStatus* status) override;
+
+    ::grpc::Status StartPropertyValuesStream(
+            ::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+            ::grpc::ServerWriter<vhal_proto::WrappedVehiclePropValue>* stream) override;
+
+  private:
+    // We keep long-lasting connection for streaming the prop values.
+    // For us, each connection can be represented as a function to send the new value, and
+    // an ID to identify this connection
+    struct ConnectionDescriptor {
+        using ValueWriterType = std::function<bool(const vhal_proto::WrappedVehiclePropValue&)>;
+
+        ConnectionDescriptor(ValueWriterType&& value_writer)
+            : mValueWriter(std::move(value_writer)),
+              mConnectionID(CONNECTION_ID_COUNTER.fetch_add(1)) {}
+
+        ConnectionDescriptor(const ConnectionDescriptor&) = delete;
+
+        ConnectionDescriptor& operator=(const ConnectionDescriptor&) = delete;
+
+        // This move constructor is NOT THREAD-SAFE, which means it cannot be moved
+        // while using. Since the connection descriptors are pretected by mConnectionMutex
+        // then we are fine here
+        ConnectionDescriptor(ConnectionDescriptor&& cd)
+            : mValueWriter(std::move(cd.mValueWriter)),
+              mConnectionID(cd.mConnectionID),
+              mIsAlive(cd.mIsAlive.load()) {
+            cd.mIsAlive.store(false);
+        }
+
+        ValueWriterType mValueWriter;
+        uint64_t mConnectionID;
+        std::atomic<bool> mIsAlive{true};
+
+        static std::atomic<uint64_t> CONNECTION_ID_COUNTER;
+    };
+
+    std::string mServiceAddr;
+    VehiclePropValuePool mValueObjectPool;
+    mutable std::shared_mutex mConnectionMutex;
+    mutable std::shared_mutex mWriterMutex;
+    std::list<ConnectionDescriptor> mValueStreamingConnections;
+};
+
+std::atomic<uint64_t> GrpcVehicleServerImpl::ConnectionDescriptor::CONNECTION_ID_COUNTER = 0;
+
+static std::shared_ptr<::grpc::ServerCredentials> getServerCredentials() {
+    // TODO(chenhaosjtuacm): get secured credentials here
+    return ::grpc::InsecureServerCredentials();
+}
+
+GrpcVehicleServerPtr makeGrpcVehicleServer(const std::string& addr) {
+    return std::make_unique<GrpcVehicleServerImpl>(addr);
+}
+
+void GrpcVehicleServerImpl::Start() {
+    ::grpc::ServerBuilder builder;
+    builder.RegisterService(this);
+    builder.AddListeningPort(mServiceAddr, getServerCredentials());
+    std::unique_ptr<::grpc::Server> server(builder.BuildAndStart());
+
+    server->Wait();
+}
+
+void GrpcVehicleServerImpl::onPropertyValueFromCar(const VehiclePropValue& value,
+                                                   bool updateStatus) {
+    vhal_proto::WrappedVehiclePropValue wrappedPropValue;
+    proto_msg_converter::toProto(wrappedPropValue.mutable_value(), value);
+    wrappedPropValue.set_update_status(updateStatus);
+    std::shared_lock read_lock(mConnectionMutex);
+
+    bool has_terminated_connections = 0;
+
+    for (auto& connection : mValueStreamingConnections) {
+        auto writeOK = connection.mValueWriter(wrappedPropValue);
+        if (!writeOK) {
+            LOG(ERROR) << __func__ << ": Server Write failed, connection lost. ID: "
+                       << connection.mConnectionID;
+            has_terminated_connections = true;
+            connection.mIsAlive.store(false);
+        }
+    }
+
+    if (!has_terminated_connections) {
+        return;
+    }
+
+    read_lock.unlock();
+
+    std::unique_lock write_lock(mConnectionMutex);
+
+    for (auto itr = mValueStreamingConnections.begin(); itr != mValueStreamingConnections.end();) {
+        if (!itr->mIsAlive.load()) {
+            itr = mValueStreamingConnections.erase(itr);
+        } else {
+            ++itr;
+        }
+    }
+}
+
+::grpc::Status GrpcVehicleServerImpl::GetAllPropertyConfig(
+        ::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+        ::grpc::ServerWriter<vhal_proto::VehiclePropConfig>* stream) {
+    auto configs = onGetAllPropertyConfig();
+    for (auto& config : configs) {
+        vhal_proto::VehiclePropConfig protoConfig;
+        proto_msg_converter::toProto(&protoConfig, config);
+        if (!stream->Write(protoConfig)) {
+            return ::grpc::Status(::grpc::StatusCode::ABORTED, "Connection lost.");
+        }
+    }
+
+    return ::grpc::Status::OK;
+}
+
+::grpc::Status GrpcVehicleServerImpl::SetProperty(
+        ::grpc::ServerContext* context, const vhal_proto::WrappedVehiclePropValue* wrappedPropValue,
+        vhal_proto::VehicleHalCallStatus* status) {
+    VehiclePropValue value;
+    proto_msg_converter::fromProto(&value, wrappedPropValue->value());
+
+    auto set_status = static_cast<int32_t>(onSetProperty(value, wrappedPropValue->update_status()));
+    if (!vhal_proto::VehicleHalStatusCode_IsValid(set_status)) {
+        return ::grpc::Status(::grpc::StatusCode::INTERNAL, "Unknown status code");
+    }
+
+    status->set_status_code(static_cast<vhal_proto::VehicleHalStatusCode>(set_status));
+
+    return ::grpc::Status::OK;
+}
+
+::grpc::Status GrpcVehicleServerImpl::StartPropertyValuesStream(
+        ::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+        ::grpc::ServerWriter<vhal_proto::WrappedVehiclePropValue>* stream) {
+    std::mutex terminateMutex;
+    std::condition_variable terminateCV;
+    std::unique_lock<std::mutex> terminateLock(terminateMutex);
+    bool terminated{false};
+
+    auto callBack = [stream, &terminateMutex, &terminateCV, &terminated,
+                     this](const vhal_proto::WrappedVehiclePropValue& value) {
+        std::unique_lock lock(mWriterMutex);
+        if (!stream->Write(value)) {
+            std::unique_lock<std::mutex> terminateLock(terminateMutex);
+            terminated = true;
+            terminateLock.unlock();
+            terminateCV.notify_all();
+            return false;
+        }
+        return true;
+    };
+
+    // Register connection
+    std::unique_lock lock(mConnectionMutex);
+    auto& conn = mValueStreamingConnections.emplace_back(std::move(callBack));
+    lock.unlock();
+
+    // Never stop until connection lost
+    terminateCV.wait(terminateLock, [&terminated]() { return terminated; });
+
+    LOG(ERROR) << __func__ << ": Stream lost, ID : " << conn.mConnectionID;
+
+    return ::grpc::Status(::grpc::StatusCode::ABORTED, "Connection lost.");
+}
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.h
new file mode 100644
index 0000000..32f4eb2
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.h
@@ -0,0 +1,49 @@
+/*
+ * 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_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleServer_H_
+#define android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleServer_H_
+
+#include "vhal_v2_0/EmulatedVehicleConnector.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+// Connect to the Vehicle Client via GRPC
+class GrpcVehicleServer : public EmulatedVehicleServer {
+  public:
+    // Start listening incoming calls, should never return if working normally
+    virtual void Start() = 0;
+};
+
+using GrpcVehicleServerPtr = std::unique_ptr<GrpcVehicleServer>;
+
+GrpcVehicleServerPtr makeGrpcVehicleServer(const std::string& addr);
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleServer_H_
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.cpp
new file mode 100644
index 0000000..41d4827
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 "Utils.h"
+
+#include <sstream>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+namespace impl {
+
+std::string getVsockUri(const VsockServerInfo& serverInfo) {
+    std::stringstream uri_stream;
+    uri_stream << "vsock:" << serverInfo.serverCid << ":" << serverInfo.serverPort;
+    return uri_stream.str();
+}
+
+}  // namespace impl
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.h
new file mode 100644
index 0000000..6b1049c
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.h
@@ -0,0 +1,43 @@
+/*
+ * 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_automotive_vehicle_V2_0_impl_virtualization_Utils_H_
+#define android_hardware_automotive_vehicle_V2_0_impl_virtualization_Utils_H_
+
+#include <string>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+namespace impl {
+
+struct VsockServerInfo {
+    unsigned int serverCid{0};
+    unsigned int serverPort{0};
+};
+
+std::string getVsockUri(const VsockServerInfo& serverInfo);
+
+}  // namespace impl
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_V2_0_impl_virtualization_Utils_H_
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 24fcf76..6145ea2 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -298,6 +298,28 @@
         | VehicleArea:SEAT),
 
     /**
+     * Exterior dimensions of vehicle.
+     *
+     *  int32Values[0] = height
+     *  int32Values[1] = length
+     *  int32Values[2] = width
+     *  int32Values[3] = width including mirrors
+     *  int32Values[4] = wheel base
+     *  int32Values[5] = track width front
+     *  int32Values[6] = track width rear
+     *  int32Values[7] = curb to curb turning radius
+     *
+     * @change_mode VehiclePropertyChangeMode:STATIC
+     * @access VehiclePropertyAccess:READ
+     * @unit VehicleUnit:MILLIMETER
+     */
+    INFO_EXTERIOR_DIMENSIONS = (
+        0x010B
+        | VehiclePropertyGroup:SYSTEM
+        | VehiclePropertyType:INT32_VEC
+        | VehicleArea:GLOBAL),
+
+    /**
      * Current odometer value of the vehicle
      *
      * @change_mode VehiclePropertyChangeMode:CONTINUOUS
@@ -340,7 +362,7 @@
         | VehicleArea:GLOBAL),
 
     /**
-     * Steering angle of the vehicle
+     * Front bicycle model steering angle for vehicle
      *
      * Angle is in degrees.  Left is negative.
      *
@@ -355,6 +377,21 @@
         | VehicleArea:GLOBAL),
 
     /**
+     * Rear bicycle model steering angle for vehicle
+     *
+     * Angle is in degrees.  Left is negative.
+     *
+     * @change_mode VehiclePropertyChangeMode:CONTINUOUS
+     * @access VehiclePropertyAccess:READ
+     * @unit VehicleUnit:DEGREES
+     */
+    PERF_REAR_STEERING_ANGLE = (
+        0x0210
+        | VehiclePropertyGroup:SYSTEM
+        | VehiclePropertyType:FLOAT
+        | VehicleArea:GLOBAL),
+
+    /**
      * Temperature of engine coolant
      *
      * @change_mode VehiclePropertyChangeMode:CONTINUOUS
diff --git a/biometrics/face/1.1/Android.bp b/biometrics/face/1.1/Android.bp
new file mode 100644
index 0000000..2206597
--- /dev/null
+++ b/biometrics/face/1.1/Android.bp
@@ -0,0 +1,17 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.biometrics.face@1.1",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "IBiometricsFace.hal",
+    ],
+    interfaces: [
+        "android.hardware.biometrics.face@1.0",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+}
diff --git a/biometrics/face/1.1/IBiometricsFace.hal b/biometrics/face/1.1/IBiometricsFace.hal
new file mode 100644
index 0000000..975001f
--- /dev/null
+++ b/biometrics/face/1.1/IBiometricsFace.hal
@@ -0,0 +1,82 @@
+/*
+ * 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.biometrics.face@1.1;
+import @1.0::IBiometricsFace;
+import @1.0::Status;
+import @1.0::Feature;
+
+/**
+ * The HAL interface for biometric face authentication.
+ */
+interface IBiometricsFace extends @1.0::IBiometricsFace {
+    /**
+     * Enrolls a user's face for a remote client, for example Android Auto.
+     *
+     * The HAL implementation is responsible for creating a secure communication
+     * channel and receiving the enrollment images from a mobile device with
+     * face authentication hardware.
+     *
+     * Note that the Hardware Authentication Token must be valid for the
+     * duration of enrollment and thus should be explicitly invalidated by a
+     * call to revokeChallenge() when enrollment is complete, to reduce the
+     * window of opportunity to re-use the challenge and HAT. For example,
+     * Settings calls generateChallenge() once to allow the user to enroll one
+     * or more faces or toggle secure settings without having to re-enter the
+     * PIN/pattern/password. Once the user completes the operation, Settings
+     * invokes revokeChallenge() to close the transaction. If the HAT is expired,
+     * the implementation must invoke onError with UNABLE_TO_PROCESS.
+     *
+     * Requirements for using this API:
+     * - Mobile devices MUST NOT delegate enrollment to another device by calling
+     * this API. This feature is intended only to allow enrollment on devices
+     * where it is impossible to enroll locally on the device.
+     * - The path MUST be protected by a secret key with rollback protection.
+     * - Synchronizing between devices MUST be accomplished by having both
+     * devices agree on a secret PIN entered by the user (similar to BT
+     * pairing procedure) and use a salted version of that PIN plus other secret
+     * to encrypt traffic.
+     * - All communication to/from the remote device MUST be encrypted and signed
+     * to prevent image injection and other man-in-the-middle type attacks.
+     * - generateChallenge() and revokeChallenge() MUST be implemented on both
+     * remote and local host (e.g. hash the result of the remote host with a
+     * local secret before responding to the API call) and any transmission of
+     * the challenge between hosts MUST be signed to prevent man-in-the-middle
+     * attacks.
+     * - In the event of a lost connection, the result of the last
+     * generateChallenge() MUST be invalidated and the process started over.
+     * - Both the remote and local host MUST honor the timeout and invalidate the
+     * challenge.
+     *
+     * This method triggers the IBiometricsFaceClientCallback#onEnrollResult()
+     * method.
+     *
+     * @param hat A valid Hardware Authentication Token, generated as a result
+     *     of a generateChallenge() challenge being wrapped by the gatekeeper
+     *     after a successful strong authentication request.
+     * @param timeoutSec A timeout in seconds, after which this enroll
+     *     attempt is cancelled. Note that the framework can continue
+     *     enrollment by calling this again with a valid HAT. This timeout is
+     *     expected to be used to limit power usage if the device becomes idle
+     *     during enrollment. The implementation is expected to send
+     *     ERROR_TIMEOUT if this happens.
+     * @param disabledFeatures A list of features to be disabled during
+     *     enrollment. Note that all features are enabled by default.
+     * @return status The status of this method call.
+     */
+    enrollRemotely(vec<uint8_t> hat, uint32_t timeoutSec,
+        vec<Feature> disabledFeatures) generates (Status status);
+};
diff --git a/biometrics/face/1.1/vts/functional/Android.bp b/biometrics/face/1.1/vts/functional/Android.bp
new file mode 100644
index 0000000..ccbb399
--- /dev/null
+++ b/biometrics/face/1.1/vts/functional/Android.bp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+    name: "VtsHalBiometricsFaceV1_1TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalBiometricsFaceV1_1TargetTest.cpp"],
+    static_libs: [
+        "android.hardware.biometrics.face@1.0",
+        "android.hardware.biometrics.face@1.1",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts-core",
+    ],
+}
diff --git a/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp b/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp
new file mode 100644
index 0000000..c2431c6
--- /dev/null
+++ b/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp
@@ -0,0 +1,163 @@
+/*
+ * 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 "biometrics_face_hidl_hal_test"
+
+#include <android/hardware/biometrics/face/1.0/IBiometricsFaceClientCallback.h>
+#include <android/hardware/biometrics/face/1.1/IBiometricsFace.h>
+
+#include <VtsHalHidlTargetCallbackBase.h>
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include <chrono>
+#include <cstdint>
+#include <random>
+
+using android::sp;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::biometrics::face::V1_0::FaceAcquiredInfo;
+using android::hardware::biometrics::face::V1_0::FaceError;
+using android::hardware::biometrics::face::V1_0::IBiometricsFaceClientCallback;
+using android::hardware::biometrics::face::V1_0::OptionalUint64;
+using android::hardware::biometrics::face::V1_0::Status;
+using android::hardware::biometrics::face::V1_1::IBiometricsFace;
+
+namespace {
+
+// Arbitrary, nonexistent userId
+constexpr uint32_t kUserId = 9;
+constexpr uint32_t kTimeoutSec = 3;
+constexpr auto kTimeout = std::chrono::seconds(kTimeoutSec);
+constexpr char kFacedataDir[] = "/data/vendor_de/0/facedata";
+constexpr char kCallbackNameOnError[] = "onError";
+
+// Callback arguments that need to be captured for the tests.
+struct FaceCallbackArgs {
+    // The error passed to the last onError() callback.
+    FaceError error;
+
+    // The userId passed to the last callback.
+    int32_t userId;
+};
+
+// Test callback class for the BiometricsFace HAL.
+// The HAL will call these callback methods to notify about completed operations
+// or encountered errors.
+class FaceCallback : public ::testing::VtsHalHidlTargetCallbackBase<FaceCallbackArgs>,
+                     public IBiometricsFaceClientCallback {
+  public:
+    Return<void> onEnrollResult(uint64_t, uint32_t, int32_t, uint32_t) override { return Void(); }
+
+    Return<void> onAuthenticated(uint64_t, uint32_t, int32_t, const hidl_vec<uint8_t>&) override {
+        return Void();
+    }
+
+    Return<void> onAcquired(uint64_t, int32_t, FaceAcquiredInfo, int32_t) override {
+        return Void();
+    }
+
+    Return<void> onError(uint64_t, int32_t userId, FaceError error, int32_t) override {
+        FaceCallbackArgs args = {};
+        args.error = error;
+        args.userId = userId;
+        NotifyFromCallback(kCallbackNameOnError, args);
+        return Void();
+    }
+
+    Return<void> onRemoved(uint64_t, const hidl_vec<uint32_t>&, int32_t) override { return Void(); }
+
+    Return<void> onEnumerate(uint64_t, const hidl_vec<uint32_t>&, int32_t) override {
+        return Void();
+    }
+
+    Return<void> onLockoutChanged(uint64_t) override { return Void(); }
+};
+
+// Test class for the BiometricsFace HAL.
+class FaceHidlTest : public ::testing::TestWithParam<std::string> {
+  public:
+    void SetUp() override {
+        mService = IBiometricsFace::getService(GetParam());
+        ASSERT_NE(mService, nullptr);
+        mCallback = new FaceCallback();
+        mCallback->SetWaitTimeoutDefault(kTimeout);
+        Return<void> ret1 = mService->setCallback(mCallback, [](const OptionalUint64& res) {
+            ASSERT_EQ(Status::OK, res.status);
+            // Makes sure the "deviceId" represented by "res.value" is not 0.
+            // 0 would mean the HIDL is not available.
+            ASSERT_NE(0UL, res.value);
+        });
+        ASSERT_TRUE(ret1.isOk());
+        Return<Status> ret2 = mService->setActiveUser(kUserId, kFacedataDir);
+        ASSERT_EQ(Status::OK, static_cast<Status>(ret2));
+    }
+
+    void TearDown() override {}
+
+    sp<IBiometricsFace> mService;
+    sp<FaceCallback> mCallback;
+};
+
+// enroll with an invalid (all zeroes) HAT should fail.
+TEST_P(FaceHidlTest, EnrollRemotelyZeroHatTest) {
+    // Filling HAT with zeros
+    hidl_vec<uint8_t> token(69);
+    for (size_t i = 0; i < 69; i++) {
+        token[i] = 0;
+    }
+
+    Return<Status> ret = mService->enrollRemotely(token, kTimeoutSec, {});
+    ASSERT_EQ(Status::OK, static_cast<Status>(ret));
+
+    // onError should be called with a meaningful (nonzero) error.
+    auto res = mCallback->WaitForCallback(kCallbackNameOnError);
+    EXPECT_TRUE(res.no_timeout);
+    EXPECT_EQ(kUserId, res.args->userId);
+    EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error);
+}
+
+// enroll with an invalid HAT should fail.
+TEST_P(FaceHidlTest, EnrollRemotelyGarbageHatTest) {
+    // Filling HAT with pseudorandom invalid data.
+    // Using default seed to make the test reproducible.
+    std::mt19937 gen(std::mt19937::default_seed);
+    std::uniform_int_distribution<uint8_t> dist;
+    hidl_vec<uint8_t> token(69);
+    for (size_t i = 0; i < 69; ++i) {
+        token[i] = dist(gen);
+    }
+
+    Return<Status> ret = mService->enrollRemotely(token, kTimeoutSec, {});
+    ASSERT_EQ(Status::OK, static_cast<Status>(ret));
+
+    // onError should be called with a meaningful (nonzero) error.
+    auto res = mCallback->WaitForCallback(kCallbackNameOnError);
+    EXPECT_TRUE(res.no_timeout);
+    EXPECT_EQ(kUserId, res.args->userId);
+    EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error);
+}
+
+}  // anonymous namespace
+
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, FaceHidlTest,
+        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBiometricsFace::descriptor)),
+        android::hardware::PrintInstanceNameToString);
diff --git a/biometrics/fingerprint/2.2/Android.bp b/biometrics/fingerprint/2.2/Android.bp
new file mode 100644
index 0000000..6c769ac
--- /dev/null
+++ b/biometrics/fingerprint/2.2/Android.bp
@@ -0,0 +1,19 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.biometrics.fingerprint@2.2",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "types.hal",
+        "IBiometricsFingerprint.hal",
+        "IBiometricsFingerprintClientCallback.hal",
+    ],
+    interfaces: [
+        "android.hardware.biometrics.fingerprint@2.1",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+}
diff --git a/biometrics/fingerprint/2.2/IBiometricsFingerprint.hal b/biometrics/fingerprint/2.2/IBiometricsFingerprint.hal
new file mode 100644
index 0000000..0651034
--- /dev/null
+++ b/biometrics/fingerprint/2.2/IBiometricsFingerprint.hal
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics.fingerprint@2.2;
+
+import @2.1::IBiometricsFingerprint;
+import @2.1::RequestStatus;
+
+interface IBiometricsFingerprint extends @2.1::IBiometricsFingerprint {
+    /**
+     * Fingerprint enroll request:
+     * Switches the HAL state machine to collect and store a new fingerprint
+     * template. Switches back as soon as enroll is complete, signalled by
+     * (fingerprintMsg.type == FINGERPRINT_TEMPLATE_ENROLLING &&
+     *  fingerprintMsg.data.enroll.samplesRemaining == 0)
+     * or after timeoutSec seconds.
+     * The fingerprint template must be assigned to the group gid.
+     *
+     * @param hat a valid Hardware Authentication Token (HAT), generated
+     * as a result of a preEnroll() call.
+     * @param gid a framework defined fingerprint set (group) id.
+     * @param timeoutSec a timeout in seconds.
+     * @param windowId optional ID of an illumination window for optical under
+     * display fingerprint sensors. Must contain a null pointer if not used.
+     *
+     * @return debugErrno is a value the framework logs in case it is not 0.
+     *
+     * A notify() function may be called with a more detailed error structure.
+     */
+    enroll_2_2(vec<uint8_t> hat, uint32_t gid, uint32_t timeoutSec, handle windowId)
+        generates (RequestStatus debugErrno);
+
+    /**
+     * Authenticates an operation identified by operationId
+     *
+     * @param operationId operation id.
+     * @param gid fingerprint group id.
+     * @param windowId optional ID of an illumination window for optical under
+     * display fingerprint sensors. Must contain a null pointer if not used.
+     *
+     * @return debugErrno is a value the framework logs in case it is not 0.
+     */
+    authenticate_2_2(uint64_t operationId, uint32_t gid, handle windowId)
+        generates (RequestStatus debugErrno);
+};
diff --git a/biometrics/fingerprint/2.2/IBiometricsFingerprintClientCallback.hal b/biometrics/fingerprint/2.2/IBiometricsFingerprintClientCallback.hal
new file mode 100644
index 0000000..14c2b12
--- /dev/null
+++ b/biometrics/fingerprint/2.2/IBiometricsFingerprintClientCallback.hal
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics.fingerprint@2.2;
+
+import @2.1::IBiometricsFingerprintClientCallback;
+
+/*
+ * This HAL interface communicates asynchronous results from the
+ * fingerprint driver in response to user actions on the fingerprint sensor
+ */
+interface IBiometricsFingerprintClientCallback extends @2.1::IBiometricsFingerprintClientCallback {
+    /**
+     * Sent when a fingerprint image is acquired by the sensor
+     * @param deviceId the instance of this fingerprint device
+     * @param acquiredInfo a message about the quality of the acquired image
+     * @param vendorCode a vendor-specific message about the quality of the image. Only
+     *        valid when acquiredInfo == ACQUIRED_VENDOR
+     */
+    oneway onAcquired_2_2(uint64_t deviceId, FingerprintAcquiredInfo acquiredInfo,
+        int32_t vendorCode);
+};
diff --git a/biometrics/fingerprint/2.2/types.hal b/biometrics/fingerprint/2.2/types.hal
new file mode 100644
index 0000000..2c1d3f3
--- /dev/null
+++ b/biometrics/fingerprint/2.2/types.hal
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics.fingerprint@2.2;
+
+import @2.1::FingerprintAcquiredInfo;
+
+/**
+ * Fingerprint acquisition info is meant as feedback for the current operation.
+ * Anything but START and ACQUIRED_GOOD must be shown to the user as feedback on
+ * how to take action on the current operation. For example,
+ * ACQUIRED_IMAGER_DIRTY may be used to tell the user to clean the sensor if it
+ * is detected to be dirty.
+ * If this causes the current operation to fail, an additional ERROR_CANCELED
+ * must be sent to stop the operation in progress (e.g. enrollment).
+ * In general, these messages will result in a "Try again" message.
+ */
+enum FingerprintAcquiredInfo : @2.1::FingerprintAcquiredInfo {
+    /**
+     * This message represents the earliest message sent at the beginning of the
+     * authentication pipeline. It is expected to be used to measure latency. For
+     * example, in a camera-based authentication system it's expected to be sent
+     * prior to camera initialization. Note this should be sent whenever
+     * authentication is restarted (see IBiometricsFace#userActivity).
+     * The framework will measure latency based on the time between the last START
+     * message and the onAuthenticated callback.
+     */
+    START = 7,
+};
diff --git a/biometrics/fingerprint/2.2/vts/functional/Android.bp b/biometrics/fingerprint/2.2/vts/functional/Android.bp
new file mode 100644
index 0000000..496570c
--- /dev/null
+++ b/biometrics/fingerprint/2.2/vts/functional/Android.bp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+    name: "VtsHalBiometricsFingerprintV2_2TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalBiometricsFingerprintV2_2TargetTest.cpp"],
+    static_libs: [
+        "android.hardware.biometrics.fingerprint@2.1",
+        "android.hardware.biometrics.fingerprint@2.2",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts-core",
+    ],
+}
diff --git a/biometrics/fingerprint/2.2/vts/functional/VtsHalBiometricsFingerprintV2_2TargetTest.cpp b/biometrics/fingerprint/2.2/vts/functional/VtsHalBiometricsFingerprintV2_2TargetTest.cpp
new file mode 100644
index 0000000..50bd4ab
--- /dev/null
+++ b/biometrics/fingerprint/2.2/vts/functional/VtsHalBiometricsFingerprintV2_2TargetTest.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "fingerprint_hidl_hal_test"
+
+#include <VtsHalHidlTargetCallbackBase.h>
+#include <android-base/properties.h>
+#include <android/hardware/biometrics/fingerprint/2.1/IBiometricsFingerprintClientCallback.h>
+#include <android/hardware/biometrics/fingerprint/2.2/IBiometricsFingerprint.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
+
+#include <cinttypes>
+#include <random>
+
+using android::sp;
+using android::base::GetUintProperty;
+using android::hardware::hidl_handle;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::biometrics::fingerprint::V2_1::FingerprintAcquiredInfo;
+using android::hardware::biometrics::fingerprint::V2_1::FingerprintError;
+using android::hardware::biometrics::fingerprint::V2_1::IBiometricsFingerprintClientCallback;
+using android::hardware::biometrics::fingerprint::V2_1::RequestStatus;
+using android::hardware::biometrics::fingerprint::V2_2::IBiometricsFingerprint;
+
+namespace {
+
+constexpr uint32_t kTimeoutSec = 3;
+constexpr auto kTimeout = std::chrono::seconds(kTimeoutSec);
+constexpr uint32_t kGroupId = 99;
+constexpr char kCallbackNameOnError[] = "onError";
+
+// Callback arguments that need to be captured for the tests.
+struct FingerprintCallbackArgs {
+    // The error passed to the last onError() callback.
+    FingerprintError error;
+
+    // The deviceId passed to the last callback.
+    uint64_t deviceId;
+};
+
+// Test callback class for the BiometricsFingerprint HAL.
+// The HAL will call these callback methods to notify about completed operations
+// or encountered errors.
+class FingerprintCallback : public ::testing::VtsHalHidlTargetCallbackBase<FingerprintCallbackArgs>,
+                            public IBiometricsFingerprintClientCallback {
+  public:
+    Return<void> onEnrollResult(uint64_t, uint32_t, uint32_t, uint32_t) override { return Void(); }
+
+    Return<void> onAcquired(uint64_t, FingerprintAcquiredInfo, int32_t) override { return Void(); }
+
+    Return<void> onAuthenticated(uint64_t, uint32_t, uint32_t, const hidl_vec<uint8_t>&) override {
+        return Void();
+    }
+
+    Return<void> onError(uint64_t deviceId, FingerprintError error, int32_t) override {
+        FingerprintCallbackArgs args = {};
+        args.error = error;
+        args.deviceId = deviceId;
+        NotifyFromCallback(kCallbackNameOnError, args);
+        return Void();
+    }
+
+    Return<void> onRemoved(uint64_t, uint32_t, uint32_t, uint32_t) override { return Void(); }
+
+    Return<void> onEnumerate(uint64_t, uint32_t, uint32_t, uint32_t) override { return Void(); }
+};
+
+class FingerprintHidlTest : public ::testing::TestWithParam<std::string> {
+  public:
+    void SetUp() override {
+        mService = IBiometricsFingerprint::getService(GetParam());
+        ASSERT_NE(mService, nullptr);
+        mCallback = new FingerprintCallback();
+        mCallback->SetWaitTimeoutDefault(kTimeout);
+        Return<uint64_t> ret1 = mService->setNotify(mCallback);
+        ASSERT_NE(0UL, static_cast<uint64_t>(ret1));
+
+        /*
+         * Devices shipped from now on will instead store
+         * fingerprint data under /data/vendor_de/<user-id>/fpdata.
+         * Support for /data/vendor_de and /data/vendor_ce has been added to vold.
+         */
+
+        auto api_level = GetUintProperty<uint64_t>("ro.product.first_api_level", 0);
+        if (api_level == 0) {
+            api_level = GetUintProperty<uint64_t>("ro.build.version.sdk", 0);
+        }
+        ASSERT_NE(api_level, 0);
+
+        // 27 is the API number for O-MR1
+        string tmpDir;
+        if (api_level <= 27) {
+            tmpDir = "/data/system/users/0/fpdata/";
+        } else {
+            tmpDir = "/data/vendor_de/0/fpdata/";
+        }
+
+        Return<RequestStatus> res = mService->setActiveGroup(kGroupId, tmpDir);
+        ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));
+    }
+
+    sp<IBiometricsFingerprint> mService;
+    sp<FingerprintCallback> mCallback;
+};
+
+// Enroll with an invalid (all zeroes) HAT should fail.
+TEST_P(FingerprintHidlTest, EnrollZeroHatTest) {
+    // Filling HAT with zeros
+    hidl_vec<uint8_t> token(69);
+    for (size_t i = 0; i < 69; i++) {
+        token[i] = 0;
+    }
+
+    hidl_handle windowId = nullptr;
+    Return<RequestStatus> ret = mService->enroll_2_2(token, kGroupId, kTimeoutSec, windowId);
+    ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(ret));
+
+    // At least one call to onError should occur
+    auto res = mCallback->WaitForCallback(kCallbackNameOnError);
+    ASSERT_NE(FingerprintError::ERROR_NO_ERROR, res.args->error);
+}
+
+// Enroll with an invalid (null) HAT should fail.
+TEST_P(FingerprintHidlTest, EnrollGarbageHatTest) {
+    // Filling HAT with pseudorandom invalid data.
+    // Using default seed to make the test reproducible.
+    std::mt19937 gen(std::mt19937::default_seed);
+    std::uniform_int_distribution<uint8_t> dist;
+    hidl_vec<uint8_t> token(69);
+    for (size_t i = 0; i < 69; ++i) {
+        token[i] = dist(gen);
+    }
+
+    hidl_handle windowId = nullptr;
+    Return<RequestStatus> ret = mService->enroll_2_2(token, kGroupId, kTimeoutSec, windowId);
+    ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(ret));
+
+    // At least one call to onError should occur
+    auto res = mCallback->WaitForCallback(kCallbackNameOnError);
+    ASSERT_NE(FingerprintError::ERROR_NO_ERROR, res.args->error);
+}
+
+}  // anonymous namespace
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, FingerprintHidlTest,
+                         testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IBiometricsFingerprint::descriptor)),
+                         android::hardware::PrintInstanceNameToString);
diff --git a/camera/metadata/3.3/types.hal b/camera/metadata/3.3/types.hal
index ca0c9d6..0d89681 100644
--- a/camera/metadata/3.3/types.hal
+++ b/camera/metadata/3.3/types.hal
@@ -71,8 +71,10 @@
 
     /** android.lens.poseReference [static, enum, public]
      *
-     * <p>The origin for ANDROID_LENS_POSE_TRANSLATION.</p>
+     * <p>The origin for ANDROID_LENS_POSE_TRANSLATION, and the accuracy of
+     * ANDROID_LENS_POSE_TRANSLATION and ANDROID_LENS_POSE_ROTATION.</p>
      *
+     * @see ANDROID_LENS_POSE_ROTATION
      * @see ANDROID_LENS_POSE_TRANSLATION
      */
     ANDROID_LENS_POSE_REFERENCE = android.hardware.camera.metadata@3.2::CameraMetadataTag:ANDROID_LENS_END,
diff --git a/camera/metadata/3.5/types.hal b/camera/metadata/3.5/types.hal
index f139088..4e2252c 100644
--- a/camera/metadata/3.5/types.hal
+++ b/camera/metadata/3.5/types.hal
@@ -104,6 +104,14 @@
     ANDROID_CONTROL_BOKEH_MODE_CONTINUOUS,
 };
 
+/** android.lens.poseReference enumeration values added since v3.3
+ * @see ANDROID_LENS_POSE_REFERENCE
+ */
+enum CameraMetadataEnumAndroidLensPoseReference :
+        @3.3::CameraMetadataEnumAndroidLensPoseReference {
+    ANDROID_LENS_POSE_REFERENCE_UNDEFINED,
+};
+
 /** android.request.availableCapabilities enumeration values added since v3.4
  * @see ANDROID_REQUEST_AVAILABLE_CAPABILITIES
  */
diff --git a/camera/provider/2.4/ICameraProvider.hal b/camera/provider/2.4/ICameraProvider.hal
index 74c3ff1..105629d 100644
--- a/camera/provider/2.4/ICameraProvider.hal
+++ b/camera/provider/2.4/ICameraProvider.hal
@@ -115,7 +115,7 @@
      *     INTERNAL_ERROR:
      *         A camera ID list cannot be created. This may be due to
      *         a failure to initialize the camera subsystem, for example.
-     * @return cameraDeviceServiceNames The vector of internal camera device
+     * @return cameraDeviceNames The vector of internal camera device
      *     names known to this provider.
      */
     getCameraIdList()
diff --git a/camera/provider/2.4/ICameraProviderCallback.hal b/camera/provider/2.4/ICameraProviderCallback.hal
index 63dd3c5..8822305 100644
--- a/camera/provider/2.4/ICameraProviderCallback.hal
+++ b/camera/provider/2.4/ICameraProviderCallback.hal
@@ -39,7 +39,7 @@
      * are already present, as soon as the callbacks are available through
      * setCallback.
      *
-     * @param cameraDeviceServiceName The name of the camera device that has a
+     * @param cameraDeviceName The name of the camera device that has a
      *     new status.
      * @param newStatus The new status that device is in.
      *
@@ -57,7 +57,7 @@
      * android.flash.info.available is reported as true via the
      * ICameraDevice::getCameraCharacteristics call.
      *
-     * @param cameraDeviceServiceName The name of the camera device that has a
+     * @param cameraDeviceName The name of the camera device that has a
      *     new status.
      * @param newStatus The new status that device is in.
      *
diff --git a/camera/provider/2.4/vts/functional/Android.bp b/camera/provider/2.4/vts/functional/Android.bp
index d14ccfa..15f5c9a 100644
--- a/camera/provider/2.4/vts/functional/Android.bp
+++ b/camera/provider/2.4/vts/functional/Android.bp
@@ -41,6 +41,7 @@
 	"android.hardware.camera.metadata@3.4",
         "android.hardware.camera.provider@2.4",
         "android.hardware.camera.provider@2.5",
+        "android.hardware.camera.provider@2.6",
         "android.hardware.graphics.common@1.0",
         "android.hidl.allocator@1.0",
         "libgrallocusage",
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index 650ec8b..b4092ca 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -40,15 +40,17 @@
 #include <android/hardware/camera/metadata/3.4/types.h>
 #include <android/hardware/camera/provider/2.4/ICameraProvider.h>
 #include <android/hardware/camera/provider/2.5/ICameraProvider.h>
+#include <android/hardware/camera/provider/2.6/ICameraProvider.h>
+#include <android/hardware/camera/provider/2.6/ICameraProviderCallback.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <binder/MemoryHeapBase.h>
 #include <cutils/properties.h>
 #include <fmq/MessageQueue.h>
 #include <grallocusage/GrallocUsageConversion.h>
+#include <gtest/gtest.h>
 #include <gui/BufferItemConsumer.h>
 #include <gui/BufferQueue.h>
 #include <gui/Surface.h>
-#include <gtest/gtest.h>
 #include <hardware/gralloc.h>
 #include <hardware/gralloc1.h>
 #include <hidl/GtestPrinter.h>
@@ -537,7 +539,7 @@
      uint32_t id;
      ASSERT_TRUE(parseProviderName(service_name, &mProviderType, &id));
 
-     castProvider(mProvider, &mProvider2_5);
+     castProvider(mProvider, &mProvider2_5, &mProvider2_6);
      notifyDeviceState(provider::V2_5::DeviceState::NORMAL);
  }
  virtual void TearDown() override {}
@@ -709,8 +711,9 @@
             sp<ICameraDeviceSession> *session /*out*/,
             camera_metadata_t **staticMeta /*out*/,
             ::android::sp<ICameraDevice> *device = nullptr/*out*/);
-    void castProvider(const sp<provider::V2_4::ICameraProvider> &provider,
-            sp<provider::V2_5::ICameraProvider> *provider2_5 /*out*/);
+    void castProvider(const sp<provider::V2_4::ICameraProvider>& provider,
+                      sp<provider::V2_5::ICameraProvider>* provider2_5 /*out*/,
+                      sp<provider::V2_6::ICameraProvider>* provider2_6 /*out*/);
     void castSession(const sp<ICameraDeviceSession> &session, int32_t deviceVersion,
             sp<device::V3_3::ICameraDeviceSession> *session3_3 /*out*/,
             sp<device::V3_4::ICameraDeviceSession> *session3_4 /*out*/,
@@ -937,6 +940,7 @@
     // Camera provider service
     sp<ICameraProvider> mProvider;
     sp<::android::hardware::camera::provider::V2_5::ICameraProvider> mProvider2_5;
+    sp<::android::hardware::camera::provider::V2_6::ICameraProvider> mProvider2_6;
 
     // Camera provider type.
     std::string mProviderType;
@@ -1649,6 +1653,33 @@
             return Void();
         }
     };
+
+    struct ProviderCb2_6
+        : public ::android::hardware::camera::provider::V2_6::ICameraProviderCallback {
+        virtual Return<void> cameraDeviceStatusChange(const hidl_string& cameraDeviceName,
+                                                      CameraDeviceStatus newStatus) override {
+            ALOGI("camera device status callback name %s, status %d", cameraDeviceName.c_str(),
+                  (int)newStatus);
+            return Void();
+        }
+
+        virtual Return<void> torchModeStatusChange(const hidl_string& cameraDeviceName,
+                                                   TorchModeStatus newStatus) override {
+            ALOGI("Torch mode status callback name %s, status %d", cameraDeviceName.c_str(),
+                  (int)newStatus);
+            return Void();
+        }
+
+        virtual Return<void> physicalCameraDeviceStatusChange(
+                const hidl_string& cameraDeviceName, const hidl_string& physicalCameraDeviceName,
+                CameraDeviceStatus newStatus) override {
+            ALOGI("physical camera device status callback name %s, physical camera name %s,"
+                  " status %d",
+                  cameraDeviceName.c_str(), physicalCameraDeviceName.c_str(), (int)newStatus);
+            return Void();
+        }
+    };
+
     sp<ProviderCb> cb = new ProviderCb;
     auto status = mProvider->setCallback(cb);
     ASSERT_TRUE(status.isOk());
@@ -1656,6 +1687,16 @@
     status = mProvider->setCallback(nullptr);
     ASSERT_TRUE(status.isOk());
     ASSERT_EQ(Status::OK, status);
+
+    if (mProvider2_6.get() != nullptr) {
+        sp<ProviderCb2_6> cb = new ProviderCb2_6;
+        auto status = mProvider2_6->setCallback(cb);
+        ASSERT_TRUE(status.isOk());
+        ASSERT_EQ(Status::OK, status);
+        status = mProvider2_6->setCallback(nullptr);
+        ASSERT_TRUE(status.isOk());
+        ASSERT_EQ(Status::OK, status);
+    }
 }
 
 // Test if ICameraProvider::getCameraDeviceInterface returns Status::OK and non-null device
@@ -5596,12 +5637,19 @@
 }
 
 //Cast camera provider to corresponding version if available
-void CameraHidlTest::castProvider(const sp<ICameraProvider> &provider,
-        sp<provider::V2_5::ICameraProvider> *provider2_5 /*out*/) {
+void CameraHidlTest::castProvider(const sp<ICameraProvider>& provider,
+                                  sp<provider::V2_5::ICameraProvider>* provider2_5 /*out*/,
+                                  sp<provider::V2_6::ICameraProvider>* provider2_6 /*out*/) {
     ASSERT_NE(nullptr, provider2_5);
-    auto castResult = provider::V2_5::ICameraProvider::castFrom(provider);
-    if (castResult.isOk()) {
-        *provider2_5 = castResult;
+    auto castResult2_5 = provider::V2_5::ICameraProvider::castFrom(provider);
+    if (castResult2_5.isOk()) {
+        *provider2_5 = castResult2_5;
+    }
+
+    ASSERT_NE(nullptr, provider2_6);
+    auto castResult2_6 = provider::V2_6::ICameraProvider::castFrom(provider);
+    if (castResult2_6.isOk()) {
+        *provider2_6 = castResult2_6;
     }
 }
 
@@ -5851,6 +5899,14 @@
         }
     }
 
+    retcode = find_camera_metadata_ro_entry(metadata,
+            ANDROID_LENS_POSE_REFERENCE, &entry);
+    if (0 == retcode && entry.count > 0) {
+        uint8_t poseReference = entry.data.u8[0];
+        ASSERT_TRUE(poseReference <= ANDROID_LENS_POSE_REFERENCE_UNDEFINED &&
+                poseReference >= ANDROID_LENS_POSE_REFERENCE_PRIMARY_CAMERA);
+    }
+
     verifyBokehCharacteristics(metadata);
     verifyZoomCharacteristics(metadata);
 }
diff --git a/camera/provider/2.6/Android.bp b/camera/provider/2.6/Android.bp
new file mode 100644
index 0000000..16bd792
--- /dev/null
+++ b/camera/provider/2.6/Android.bp
@@ -0,0 +1,25 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.camera.provider@2.6",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "ICameraProvider.hal",
+        "ICameraProviderCallback.hal",
+    ],
+    interfaces: [
+        "android.hardware.camera.common@1.0",
+        "android.hardware.camera.device@1.0",
+        "android.hardware.camera.device@3.2",
+        "android.hardware.camera.device@3.3",
+        "android.hardware.camera.device@3.4",
+        "android.hardware.camera.provider@2.4",
+        "android.hardware.camera.provider@2.5",
+        "android.hardware.graphics.common@1.0",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: false,
+}
diff --git a/camera/provider/2.6/ICameraProvider.hal b/camera/provider/2.6/ICameraProvider.hal
new file mode 100644
index 0000000..60b59a3
--- /dev/null
+++ b/camera/provider/2.6/ICameraProvider.hal
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera.provider@2.6;
+
+import @2.5::ICameraProvider;
+
+/**
+ * Camera provider HAL
+ */
+interface ICameraProvider extends @2.5::ICameraProvider {
+    /**
+     * @2.4::ICameraProvider::setCallback can be passed a
+     * @2.6::ICameraProviderCallback to receive physical camera availability
+     * callbacks for logical multi-cameras.
+     */
+};
diff --git a/camera/provider/2.6/ICameraProviderCallback.hal b/camera/provider/2.6/ICameraProviderCallback.hal
new file mode 100644
index 0000000..42c1092
--- /dev/null
+++ b/camera/provider/2.6/ICameraProviderCallback.hal
@@ -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.camera.provider@2.6;
+
+import android.hardware.camera.common@1.0::types;
+import android.hardware.camera.provider@2.4::ICameraProviderCallback;
+
+/**
+ * Callback functions for a camera provider HAL to use to inform the camera
+ * service of changes to the camera subsystem.
+ *
+ * Version 2.6 adds support for physical camera device status callback for
+ * multi-camera.
+ */
+interface ICameraProviderCallback extends @2.4::ICameraProviderCallback {
+
+    /**
+     * cameraPhysicalDeviceStatusChange:
+     *
+     * Callback to the camera service to indicate that the state of a physical
+     * camera device of a logical multi-camera has changed.
+     *
+     * On camera service startup, when ICameraProvider::setCallback is invoked,
+     * the camera service must assume that all physical devices backing internal
+     * multi-camera devices are in the CAMERA_DEVICE_STATUS_PRESENT state.
+     *
+     * The provider must call this method to inform the camera service of any
+     * initially NOT_PRESENT physical devices, as soon as the callbacks are available
+     * through setCallback.
+     *
+     * @param cameraDeviceName The name of the logical multi-camera whose
+     *     physical camera has a new status.
+     * @param physicalCameraDeviceName The name of the physical camera device
+     *     that has a new status.
+     * @param newStatus The new status that device is in.
+     *
+     */
+    physicalCameraDeviceStatusChange(string cameraDeviceName,
+            string physicalCameraDeviceName, CameraDeviceStatus newStatus);
+};
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 1e3f743..c10ca27 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -122,7 +122,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.camera.provider</name>
-        <version>2.4-5</version>
+        <version>2.4-6</version>
         <interface>
             <name>ICameraProvider</name>
             <regex-instance>[^/]+/[0-9]+</regex-instance>
@@ -174,7 +174,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.dumpstate</name>
-        <version>1.0</version>
+        <version>1.1</version>
         <interface>
             <name>IDumpstateDevice</name>
             <instance>default</instance>
@@ -204,6 +204,8 @@
     </hal>
     <hal format="hidl" optional="false">
         <name>android.hardware.graphics.allocator</name>
+        <!-- TODO(b/136016160): deprecating 2.0. New devices should not use this -->
+        <version>2.0</version>
         <version>3.0</version>
         <version>4.0</version>
         <interface>
@@ -221,6 +223,8 @@
     </hal>
     <hal format="hidl" optional="false">
         <name>android.hardware.graphics.mapper</name>
+        <!-- TODO(b/136016160): deprecating 2.1. New devices should not use this -->
+        <version>2.1</version>
         <version>3.0</version>
         <version>4.0</version>
         <interface>
@@ -285,6 +289,13 @@
             <instance>default</instance>
         </interface>
     </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.light</name>
+        <interface>
+            <name>ILights</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.media.c2</name>
         <version>1.0-1</version>
@@ -338,9 +349,8 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
+    <hal format="aidl" optional="true">
         <name>android.hardware.power</name>
-        <version>1.0-3</version>
         <interface>
             <name>IPower</name>
             <instance>default</instance>
@@ -472,7 +482,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.usb.gadget</name>
-        <version>1.0</version>
+        <version>1.0-1</version>
         <interface>
             <name>IUsbGadget</name>
             <instance>default</instance>
diff --git a/current.txt b/current.txt
index 932c6e9..f6a3a87 100644
--- a/current.txt
+++ b/current.txt
@@ -586,6 +586,10 @@
 d3a344b7bd4c0d2658ae7209f55a979b8f53f361fd00f4fca29d5baa56d11fd2 android.hardware.automotive.evs@1.0::types
 2410dd02d67786a732d36e80b0f8ccf55086604ef37f9838e2013ff2c571e404 android.hardware.camera.device@3.5::types
 cd06a7911b9acd4a653bbf7133888878fbcb3f84be177c7a3f1becaae3d8618f android.hardware.camera.metadata@3.2::types
+a05277065c28ebecd58118bd240fb8c55757361e8648c01f7c4dacdb7f2a95dc android.hardware.camera.metadata@3.3::types
+9cb3df2bde2c6cd5fd96b7c41555420cacd7e276a556c684af91b7461c86460f android.hardware.gnss@1.0::IGnssCallback
+bceee81ec1b59324abd05932b5620fda5a6589597c9cb3953ba7f3ea02cccd3e android.hardware.camera.provider@2.4::ICameraProvider
+2ce820dc4f3c6d85721b65150ed2157c6e2e2055f866fb6c6ba4790f14408d66 android.hardware.camera.provider@2.4::ICameraProviderCallback
 b69a7615c508acf5c5201efd1bfa3262167874fc3594e2db5a3ff93addd8ac75 android.hardware.keymaster@4.0::IKeymasterDevice
 eb2fa0c883c2185d514be0b84c179b283753ef0c1b77b45b4f359bd23bba8b75 android.hardware.neuralnetworks@1.0::IPreparedModel
 8eac60e1f724d141c71c69f06d4544acb720a55dfbbcd97fa01bb3d25ee4e2f5 android.hardware.neuralnetworks@1.0::types
@@ -606,7 +610,7 @@
 2df5d5866b37776f25079c0e54b54350a2abe4e025a59c9e02a7d3abe8ca00e8 android.hardware.audio@6.0::IStreamIn
 78e4138cc8307c11fc777c3bd376e581ba4ba48196b05ca1d7cdfa515c87b48a android.hardware.audio@6.0::IStreamOut
 997fdaad7a9d17ee7e01feb7031a753e2365e72ad30b11d950e9183fabdf3844 android.hardware.audio@6.0::IStreamOutCallback
-8c4232772efeb9905b4c287723e0ee8b2c4bf5ba11728d051171b070e3d79144 android.hardware.audio.common@6.0::types
+43a3303378f5b9852c2da9ac2c1d440965aab7ba02a800229e7b3c84e2167e06 android.hardware.audio.common@6.0::types
 817930d58412d662cb45e641c50cb62c727e4a3e3ffe7029a53cad9677b97d58 android.hardware.audio.effect@6.0::types
 525bec6b44f1103869c269a128d51b8dccd73af5340ba863c8886c68357c7faf android.hardware.audio.effect@6.0::IAcousticEchoCancelerEffect
 8d76bbe3719d051a8e9a1dcf9244f37f5b0a491feb249fa48391edf7cb4f3131 android.hardware.audio.effect@6.0::IAutomaticGainControlEffect
@@ -632,44 +636,55 @@
 4d85e814f94949dae4dc6cb82bbd7d6bb24ffafda6ddb2eac928d2a4fc2e21ce android.hardware.cas@1.2::types
 66931c2506fbb5af61f20138cb05e0a09e7bf67d6964c231d27c648933bb33ec android.hardware.drm@1.3::ICryptoFactory
 994d08ab27d613022c258a9ec48cece7adf2a305e92df5d76ef923e2c6665f64 android.hardware.drm@1.3::IDrmFactory
+881aa8720fb1d69aa9843bfab69d810ab7654a61d2f5ab5e2626cbf240f24eaf android.hardware.dumpstate@1.1::types
+13b33f623521ded51a6c0f7ea5b77e97066d0aa1e38a83c2873f08ad67294f89 android.hardware.dumpstate@1.1::IDumpstateDevice
+769d346927a94fd40ee80a5a976d8d15cf022ef99c5900738f4a82f26c0ed229 android.hardware.gnss@2.1::types
 3dacec7801968e1e4479724dc0180442d9e915466bff051f80996266b1a51c2c android.hardware.gnss@2.1::IGnss
 ba62e1e8993bfb9f27fa04816fa0f2241ae2d01edfa3d0c04182e2e5de80045c android.hardware.gnss@2.1::IGnssCallback
 ccdf3c0fb2c02a6d4dc57afb276c3497ae8172b80b00ebc0bf8a0238dd38b01d android.hardware.gnss@2.1::IGnssConfiguration
 5a125c49ca83629e22afc8c39e865509343bfa2c38f0baea9a186bbac103492d android.hardware.gnss@2.1::IGnssMeasurement
-0bfb291708dd4a7c6ec6b9883e2b8592357edde8d7e962ef83918e4a2154ce69 android.hardware.gnss@2.1::IGnssMeasurementCallback
+d7bf37660a0946de9599dcbae997b077ee3e604fc2044534d40d3da04297a5d3 android.hardware.gnss@2.1::IGnssMeasurementCallback
 ce8dbe76eb9ee94b46ef98f725be992e760a5751073d4f4912484026541371f3 android.hardware.health@2.1::IHealth
 26f04510a0b57aba5167c5c0a7c2f077c2acbb98b81902a072517829fd9fd67f android.hardware.health@2.1::IHealthInfoCallback
 db47f4ceceb1f06c656f39caa70c557b0f8471ef59fd58611bea667ffca20101 android.hardware.health@2.1::types
-c228aaa27f66c48e147159a4f4996c5273191fece1b08de31bd171c61334855e android.hardware.keymaster@4.1::IKeymasterDevice
+0589e410f519e36514e7ece18f283f022df0f70efd2c12821d822f67f74aba98 android.hardware.identity@1.0::types
+bbeee9604128ede83ee755b67e73b5ad29e6e1dbac9ec41fea6ffe2745b0c50a android.hardware.identity@1.0::IIdentityCredential
+96ce8aad80f4c476f25261f790d357c117e79e18474c7dadd850dac704bbe65e android.hardware.identity@1.0::IIdentityCredentialStore
+6e1e28a96c90ba78d47257faea3f3bb4e6360affbbfa5822f0dc31211f9266ff android.hardware.identity@1.0::IWritableIdentityCredential
+27ae3724053940462114228872b3ffaf0b8e6177d5ba97f5a76339d12b8a99dd android.hardware.keymaster@4.1::IKeymasterDevice
 adb0efdf1462e9b2e742c0dcadd598666aac551f178be06e755bfcdf5797abd0 android.hardware.keymaster@4.1::IOperation
-7a04ea5595ed418ca3e91c28b8bd7353dd988be9be7b0c8c9e64fb4b77bd4523 android.hardware.keymaster@4.1::types
+ddcf89cd8ee2df0d32aee55050826446fb64f7aafde0a7cd946c64f61b1a364c android.hardware.keymaster@4.1::types
 df9c79c4fdde2821550c6d5c3d07f5ec0adfb1b702561ce543c906ddef698703 android.hardware.media.c2@1.1::IComponent
 a3eddd9bbdc87e8c22764070037dd1154f1cf006e6fba93364c4f85d4c134a19 android.hardware.media.c2@1.1::IComponentStore
-9e59fffceed0dd72a9799e04505db5f777bbbea1af0695ba4107ef6d967c6fda android.hardware.neuralnetworks@1.3::IDevice
-258825966435b3ed08832055bb736d81516013e405f161d9ccde9a90cfcdde83 android.hardware.neuralnetworks@1.3::IPreparedModel
-94e803236398bed1febb11cc21051bc42ec003700139b099d6c479e02a7ca3c3 android.hardware.neuralnetworks@1.3::IPreparedModelCallback
-f3c1e7298da628a755b452cd3325e8d0fe867a2debb873069baab6a27434a72d android.hardware.neuralnetworks@1.3::types
+65c16331e57f6dd68b3971f06f78fe9e3209afb60630c31705aa355f9a52bf0d android.hardware.neuralnetworks@1.3::IBuffer
+d1f382d14e1384b907d5bb5780df7f01934650d556fedbed2f15a90773c657d6 android.hardware.neuralnetworks@1.3::IDevice
+4167dc3ad35e9cd0d2057d4868c7675ae2c3c9d05bbd614c1f5dccfa5fd68797 android.hardware.neuralnetworks@1.3::IExecutionCallback
+29e26e83399b69c7998b787bd30426dd5baa2da350effca76bbee1ba877355c9 android.hardware.neuralnetworks@1.3::IFencedExecutionCallback
+384fd9fd6e4d43ea11d407e52ea81da5242c3c5f4b458b8707d8feb652a13e36 android.hardware.neuralnetworks@1.3::IPreparedModel
+0439a1fbbec7f16e5e4c653d85ac685d51bfafbae15b8f8cca530acdd7d6a8ce android.hardware.neuralnetworks@1.3::IPreparedModelCallback
+5f1a4e0c29fc686ed476f9f04eed35e4405d21288cb2746b978d6891de5cc37d android.hardware.neuralnetworks@1.3::types
 3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi
-42e72d7c8fd843d2611ffb9142bfae61dcdb5325860c6602825863f086a91bff android.hardware.wifi.hostapd@1.2::IHostapd
+c67aaf26a7a40d14ea61e70e20afacbd0bb906df1704d585ac8599fbb69dd44b android.hardware.wifi.hostapd@1.2::IHostapd
 11f6448d15336361180391c8ebcdfd2d7cf77b3782d577e594d583aadc9c2877 android.hardware.wifi.hostapd@1.2::types
 a64467bae843569f0d465c5be7f0c7a5b987985b55a3ef4794dd5afc68538650 android.hardware.wifi.supplicant@1.3::ISupplicant
-c72cb37b3f66ef65aeb5c6438a3fbe17bbe847fdf62d1a76eafd7f3a8a526105 android.hardware.wifi.supplicant@1.3::ISupplicantStaIface
-342a8e12db4dca643f2755eb4167e8f103d96502053a25a1f51f42107a4530f1 android.hardware.wifi.supplicant@1.3::ISupplicantStaIfaceCallback
-8835e9799cddf7c239f60beff467cbdf164331f70a8b6c06ed78982d7810d835 android.hardware.wifi.supplicant@1.3::ISupplicantStaNetwork
-91015479f5a0fba9872e98d3cca4680995de64f42ae71461b4b7e5acc5a196ab android.hardware.wifi.supplicant@1.3::types
+8aed0a8e03e7a67bfdfb78ad7529a9ae95bea36e6060473b204c89d772522126 android.hardware.wifi.supplicant@1.3::ISupplicantStaIface
+def77c7db95d374f11a111bfc4ed60f92451303642a43276c4e291988fcee625 android.hardware.wifi.supplicant@1.3::ISupplicantStaIfaceCallback
+62cf050c593c1ec34b49178b5bdde72dd9b80d9bad3eb184e4f0cd564d28678c android.hardware.wifi.supplicant@1.3::ISupplicantStaNetwork
+98592d193a717066facf91428426e5abe211e3bd718bc372e29fb944ddbe6e7c android.hardware.wifi.supplicant@1.3::types
 ##
 # BEGIN Radio HAL Merge Conflict Avoidance Buffer - STOPSHIP if present
 ##
-73b5418353fe52721267d64592d4d4c1b77fbd1ef4261d964865de88e62ee0be android.hardware.radio@1.5::types
-996f98ffe508a2f6f1755c1511b50067f7883f7c445dea9f3e931385f020b7ab android.hardware.radio@1.5::IRadio
-20d52e66fd548f89bcb98cda42749a591ce8f439a2a7148617adac0c967ad937 android.hardware.radio@1.5::IRadioIndication
-1512f6e1198e1aa0ebcbdb1694d0ed500a3e7791d6f305327866112331d82b66 android.hardware.radio@1.5::IRadioResponse
-55f0a15642869ec98a55ea0a5ac049d3e1a6245ff7750deb6bcb7182057eee83 android.hardware.radio.config@1.3::types
-b27ab0cd40b0b078cdcd024bfe1061c4c4c065f3519eeb9347fa359a3268a5ae android.hardware.radio.config@1.3::IRadioConfig
+3a303604d6c99a36c3d2a819b7908b3681b8488f89c26ea525768522c45f7f17 android.hardware.radio@1.5::types
+26216f3566aff76d8a29ee95f74bcb099a05f65ead8d6d4fadafee6967889b93 android.hardware.radio@1.5::IRadio
+e96ae1c3a9c0689002ec2318e9c587f4f607c16a75a3cd38788b77eb91072021 android.hardware.radio@1.5::IRadioIndication
+1a3324125cae8f4ca9984225d2f14bafeb835b8d9a1717fc9ed794de701f197c android.hardware.radio@1.5::IRadioResponse
+2fd107f3de1b7e36825e241a88dfae8edf3a77c166cb746f00ddf6440ab78db1 android.hardware.radio.config@1.3::types
+a2977755bc5f1ef47f04b7f2400632efda6218e1515dba847da487145cfabc4f android.hardware.radio.config@1.3::IRadioConfig
 742360c775313438b0f82256eac62fb5bbc76a6ae6f388573f3aa142fb2c1eea android.hardware.radio.config@1.3::IRadioConfigIndication
-7683fed9d253956071f18b152e6be657719536f98d9b534433d5e411bcde5061 android.hardware.radio.config@1.3::IRadioConfigResponse
+0006ab8e8b0910cbd3bbb08d5f17d5fac7d65a2bdad5f2334e4851db9d1e6fa8 android.hardware.radio.config@1.3::IRadioConfigResponse
 ##
 # END Radio HAL Merge Conflict Avoidance Buffer - STOPSHIP if present
 ##
 51d1c8d285e0456da2a3fdfbf4700c6277165d5e83219894d651c8ea0e39aa8b android.hardware.soundtrigger@2.3::types
 12d7533ff0754f45bf59ab300799074570a99a676545652c2c23abc73cb4515d android.hardware.soundtrigger@2.3::ISoundTriggerHw
+7746fda1fbf9c7c132bae701cc5a161309e4f5e7f3e8065811045975ee86196d android.hardware.usb.gadget@1.1::IUsbGadget
diff --git a/drm/1.0/vts/functional/Android.bp b/drm/1.0/vts/functional/Android.bp
index 61d4d58..4fbd54d 100644
--- a/drm/1.0/vts/functional/Android.bp
+++ b/drm/1.0/vts/functional/Android.bp
@@ -20,7 +20,7 @@
     srcs: [
         "drm_hal_clearkey_test.cpp",
         "drm_hal_vendor_test.cpp",
-        "vendor_modules.cpp"
+        "vendor_modules.cpp",
     ],
     static_libs: [
         "android.hardware.drm@1.0",
@@ -32,5 +32,8 @@
         "libssl",
         "libcrypto_static",
     ],
-    test_suites: ["general-tests"],
+    test_suites: [
+        "general-tests",
+        "vts-core",
+    ],
 }
diff --git a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
index 4a1892b..5713d2e 100644
--- a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
+++ b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
@@ -23,15 +23,15 @@
 #include <android/hardware/drm/1.0/types.h>
 #include <android/hidl/allocator/1.0/IAllocator.h>
 #include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
 #include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
 #include <hidlmemory/mapping.h>
 #include <log/log.h>
-#include <memory>
 #include <openssl/aes.h>
+#include <memory>
 #include <random>
 
-#include "VtsHalHidlTargetTestBase.h"
-
 using ::android::hardware::drm::V1_0::BufferType;
 using ::android::hardware::drm::V1_0::DestinationBuffer;
 using ::android::hardware::drm::V1_0::ICryptoFactory;
@@ -89,44 +89,59 @@
     0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
     0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
 
-class DrmHalClearkeyFactoryTest : public ::testing::VtsHalHidlTargetTestBase {
-   public:
-    virtual void SetUp() override {
+class DrmHalClearkeyFactoryTest : public ::testing::TestWithParam<std::string> {
+  public:
+    void SetUp() override {
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
         ALOGD("Running test %s.%s", test_info->test_case_name(),
               test_info->name());
 
-        drmFactory =
-                ::testing::VtsHalHidlTargetTestBase::getService<IDrmFactory>();
+        const std::string instanceName = GetParam();
+        drmFactory = IDrmFactory::getService(instanceName);
         ASSERT_NE(nullptr, drmFactory.get());
-        cryptoFactory =
-                ::testing::VtsHalHidlTargetTestBase::getService<ICryptoFactory>();
+        cryptoFactory = ICryptoFactory::getService(instanceName);
         ASSERT_NE(nullptr, cryptoFactory.get());
-    }
 
-    virtual void TearDown() override {}
+        const bool drmClearKey = drmFactory->isCryptoSchemeSupported(kClearKeyUUID);
+        const bool cryptoClearKey = cryptoFactory->isCryptoSchemeSupported(kClearKeyUUID);
+        EXPECT_EQ(drmClearKey, cryptoClearKey);
+        const bool supportsClearKey = drmClearKey && cryptoClearKey;
+
+        const bool drmCommonPsshBox = drmFactory->isCryptoSchemeSupported(kCommonPsshBoxUUID);
+        const bool cryptoCommonPsshBox = cryptoFactory->isCryptoSchemeSupported(kCommonPsshBoxUUID);
+        EXPECT_EQ(drmCommonPsshBox, cryptoCommonPsshBox);
+        const bool supportsCommonPsshBox = drmCommonPsshBox && cryptoCommonPsshBox;
+
+        EXPECT_EQ(supportsClearKey, supportsCommonPsshBox);
+        correspondsToThisTest = supportsClearKey && supportsCommonPsshBox;
+
+        if (instanceName == "clearkey") {
+            EXPECT_TRUE(correspondsToThisTest);
+
+            // TODO(b/147449315)
+            // Only the clearkey plugged into the "default" instance supports
+            // this test. Currently the "clearkey" instance fails some tests
+            // here.
+            GTEST_SKIP() << "Clearkey tests don't work with 'clearkey' instance yet.";
+        }
+
+        if (!correspondsToThisTest) {
+            GTEST_SKIP() << "Cannot test clearkey features on non-clearkey DRM modules";
+        }
+    }
 
    protected:
     sp<IDrmFactory> drmFactory;
     sp<ICryptoFactory> cryptoFactory;
+
+    bool correspondsToThisTest;
 };
 
 /**
- * Ensure the factory supports both Common Pssh Box UUID and Clearkey Scheme UUID
- */
-TEST_F(DrmHalClearkeyFactoryTest, ClearKeyPluginSupported) {
-    EXPECT_TRUE(drmFactory->isCryptoSchemeSupported(kCommonPsshBoxUUID));
-    EXPECT_TRUE(cryptoFactory->isCryptoSchemeSupported(kCommonPsshBoxUUID));
-
-    EXPECT_TRUE(drmFactory->isCryptoSchemeSupported(kClearKeyUUID));
-    EXPECT_TRUE(cryptoFactory->isCryptoSchemeSupported(kClearKeyUUID));
-}
-
-/**
  * Ensure the factory doesn't support an invalid scheme UUID
  */
-TEST_F(DrmHalClearkeyFactoryTest, InvalidPluginNotSupported) {
+TEST_P(DrmHalClearkeyFactoryTest, InvalidPluginNotSupported) {
     EXPECT_FALSE(drmFactory->isCryptoSchemeSupported(kInvalidUUID));
     EXPECT_FALSE(cryptoFactory->isCryptoSchemeSupported(kInvalidUUID));
 }
@@ -134,7 +149,7 @@
 /**
  * Ensure the factory doesn't support an empty UUID
  */
-TEST_F(DrmHalClearkeyFactoryTest, EmptyPluginUUIDNotSupported) {
+TEST_P(DrmHalClearkeyFactoryTest, EmptyPluginUUIDNotSupported) {
     hidl_array<uint8_t, 16> emptyUUID;
     memset(emptyUUID.data(), 0, 16);
     EXPECT_FALSE(drmFactory->isCryptoSchemeSupported(emptyUUID));
@@ -144,7 +159,7 @@
 /**
  * Ensure empty content type is not supported
  */
-TEST_F(DrmHalClearkeyFactoryTest, EmptyContentTypeNotSupported) {
+TEST_P(DrmHalClearkeyFactoryTest, EmptyContentTypeNotSupported) {
     hidl_string empty;
     EXPECT_FALSE(drmFactory->isContentTypeSupported(empty));
 }
@@ -152,7 +167,7 @@
 /**
  * Ensure invalid content type is not supported
  */
-TEST_F(DrmHalClearkeyFactoryTest, InvalidContentTypeNotSupported) {
+TEST_P(DrmHalClearkeyFactoryTest, InvalidContentTypeNotSupported) {
     hidl_string invalid("abcdabcd");
     EXPECT_FALSE(drmFactory->isContentTypeSupported(invalid));
 }
@@ -160,7 +175,7 @@
 /**
  * Ensure valid content type is supported
  */
-TEST_F(DrmHalClearkeyFactoryTest, ValidContentTypeSupported) {
+TEST_P(DrmHalClearkeyFactoryTest, ValidContentTypeSupported) {
     hidl_string cencType("cenc");
     EXPECT_TRUE(drmFactory->isContentTypeSupported(cencType));
 }
@@ -168,7 +183,7 @@
 /**
  * Ensure clearkey drm plugin can be created using Common Pssh Box UUID
  */
-TEST_F(DrmHalClearkeyFactoryTest, CreateClearKeyDrmPluginUsingCommonPsshBoxUuid) {
+TEST_P(DrmHalClearkeyFactoryTest, CreateClearKeyDrmPluginUsingCommonPsshBoxUuid) {
     hidl_string packageName("android.hardware.drm.test");
     auto res = drmFactory->createPlugin(
             kCommonPsshBoxUUID, packageName,
@@ -182,7 +197,7 @@
 /**
  * Ensure clearkey drm plugin can be created using ClearKey UUID
  */
- TEST_F(DrmHalClearkeyFactoryTest, CreateClearKeyDrmPluginUsingClearKeyUuid) {
+TEST_P(DrmHalClearkeyFactoryTest, CreateClearKeyDrmPluginUsingClearKeyUuid) {
     hidl_string packageName("android.hardware.drm.test");
     auto res = drmFactory->createPlugin(
             kClearKeyUUID, packageName,
@@ -196,7 +211,7 @@
 /**
  * Ensure clearkey crypto plugin can be created using Common Pssh Box UUID
  */
-TEST_F(DrmHalClearkeyFactoryTest, CreateClearKeyCryptoPluginUsingCommonPsshBoxUuid) {
+TEST_P(DrmHalClearkeyFactoryTest, CreateClearKeyCryptoPluginUsingCommonPsshBoxUuid) {
     hidl_vec<uint8_t> initVec;
     auto res = cryptoFactory->createPlugin(
             kCommonPsshBoxUUID, initVec,
@@ -210,7 +225,7 @@
 /**
  * Ensure clearkey crypto plugin can be created using ClearKey UUID
  */
-TEST_F(DrmHalClearkeyFactoryTest, CreateClearKeyCryptoPluginUsingClearKeyUuid) {
+TEST_P(DrmHalClearkeyFactoryTest, CreateClearKeyCryptoPluginUsingClearKeyUuid) {
     hidl_vec<uint8_t> initVec;
     auto res = cryptoFactory->createPlugin(
             kClearKeyUUID, initVec,
@@ -224,7 +239,7 @@
 /**
  * Ensure invalid drm plugin can't be created
  */
-TEST_F(DrmHalClearkeyFactoryTest, CreateInvalidDrmPlugin) {
+TEST_P(DrmHalClearkeyFactoryTest, CreateInvalidDrmPlugin) {
     hidl_string packageName("android.hardware.drm.test");
     auto res = drmFactory->createPlugin(
             kInvalidUUID, packageName,
@@ -238,7 +253,7 @@
 /**
  * Ensure invalid crypto plugin can't be created
  */
-TEST_F(DrmHalClearkeyFactoryTest, CreateInvalidCryptoPlugin) {
+TEST_P(DrmHalClearkeyFactoryTest, CreateInvalidCryptoPlugin) {
     hidl_vec<uint8_t> initVec;
     auto res = cryptoFactory->createPlugin(
             kInvalidUUID, initVec,
@@ -255,6 +270,10 @@
         // Create factories
         DrmHalClearkeyFactoryTest::SetUp();
 
+        if (!correspondsToThisTest) {
+            GTEST_SKIP() << "Cannot test clearkey features on non-clearkey DRM modules";
+        }
+
         ASSERT_NE(nullptr, drmFactory.get());
         hidl_string packageName("android.hardware.drm.test");
         auto res = drmFactory->createPlugin(
@@ -277,8 +296,6 @@
         ASSERT_OK(res);
     }
 
-    virtual void TearDown() override {}
-
     SessionId openSession();
     void closeSession(const SessionId& sessionId);
     hidl_vec<uint8_t> loadKeys(const SessionId& sessionId, const KeyType& type);
@@ -298,7 +315,7 @@
  * the clearkey plugin doesn't support provisioning, it is
  * expected to return Status::ERROR_DRM_CANNOT_HANDLE.
  */
-TEST_F(DrmHalClearkeyPluginTest, GetProvisionRequest) {
+TEST_P(DrmHalClearkeyPluginTest, GetProvisionRequest) {
     hidl_string certificateType;
     hidl_string certificateAuthority;
     auto res = drmPlugin->getProvisionRequest(
@@ -314,7 +331,7 @@
  * The DRM HAL should return BAD_VALUE if an empty provisioning
  * response is provided.
  */
-TEST_F(DrmHalClearkeyPluginTest, ProvideEmptyProvisionResponse) {
+TEST_P(DrmHalClearkeyPluginTest, ProvideEmptyProvisionResponse) {
     hidl_vec<uint8_t> response;
     auto res = drmPlugin->provideProvisionResponse(
             response, [&](Status status, const hidl_vec<uint8_t>&,
@@ -412,7 +429,7 @@
 /**
  * Test that a session can be opened and closed
  */
-TEST_F(DrmHalClearkeyPluginTest, OpenCloseSession) {
+TEST_P(DrmHalClearkeyPluginTest, OpenCloseSession) {
     auto sessionId = openSession();
     closeSession(sessionId);
 }
@@ -421,7 +438,7 @@
  * Test that attempting to close an invalid (empty) sessionId
  * is prohibited with the documented error code.
  */
-TEST_F(DrmHalClearkeyPluginTest, CloseInvalidSession) {
+TEST_P(DrmHalClearkeyPluginTest, CloseInvalidSession) {
     SessionId invalidSessionId;
     Status result = drmPlugin->closeSession(invalidSessionId);
     EXPECT_EQ(Status::BAD_VALUE, result);
@@ -431,7 +448,7 @@
  * Test that attempting to close a session that is already closed
  * is prohibited with the documented error code.
  */
-TEST_F(DrmHalClearkeyPluginTest, CloseClosedSession) {
+TEST_P(DrmHalClearkeyPluginTest, CloseClosedSession) {
     SessionId sessionId = openSession();
     closeSession(sessionId);
     Status result = drmPlugin->closeSession(sessionId);
@@ -441,7 +458,7 @@
 /**
  * A get key request should fail if no sessionId is provided
  */
-TEST_F(DrmHalClearkeyPluginTest, GetKeyRequestNoSession) {
+TEST_P(DrmHalClearkeyPluginTest, GetKeyRequestNoSession) {
     SessionId invalidSessionId;
     hidl_vec<uint8_t> initData;
     hidl_string mimeType = "video/mp4";
@@ -459,7 +476,7 @@
  * Test that the plugin returns the expected error code in
  * this case.
  */
-TEST_F(DrmHalClearkeyPluginTest, GetKeyRequestOfflineKeyTypeNotSupported) {
+TEST_P(DrmHalClearkeyPluginTest, GetKeyRequestOfflineKeyTypeNotSupported) {
     auto sessionId = openSession();
     hidl_vec<uint8_t> initData;
     hidl_string mimeType = "video/mp4";
@@ -481,7 +498,7 @@
  * case of attempting to generate a key request using an
  * invalid mime type
  */
-TEST_F(DrmHalClearkeyPluginTest, GetKeyRequestBadMime) {
+TEST_P(DrmHalClearkeyPluginTest, GetKeyRequestBadMime) {
     auto sessionId = openSession();
     hidl_vec<uint8_t> initData;
     hidl_string mimeType = "video/unknown";
@@ -499,7 +516,7 @@
 /**
  * Test that a closed sessionID returns SESSION_NOT_OPENED
  */
-TEST_F(DrmHalClearkeyPluginTest, ProvideKeyResponseClosedSession) {
+TEST_P(DrmHalClearkeyPluginTest, ProvideKeyResponseClosedSession) {
     SessionId session = openSession();
     closeSession(session);
 
@@ -517,7 +534,7 @@
 /**
  * Test that an empty sessionID returns BAD_VALUE
  */
-TEST_F(DrmHalClearkeyPluginTest, ProvideKeyResponseInvalidSessionId) {
+TEST_P(DrmHalClearkeyPluginTest, ProvideKeyResponseInvalidSessionId) {
     SessionId session;
 
     hidl_vec<uint8_t> keyResponse = {0x7b, 0x22, 0x6b, 0x65,
@@ -534,7 +551,7 @@
 /**
  * Test that an empty key response returns BAD_VALUE
  */
-TEST_F(DrmHalClearkeyPluginTest, ProvideKeyResponseEmptyResponse) {
+TEST_P(DrmHalClearkeyPluginTest, ProvideKeyResponseEmptyResponse) {
     SessionId session = openSession();
     hidl_vec<uint8_t> emptyResponse;
     auto res = drmPlugin->provideKeyResponse(
@@ -550,7 +567,7 @@
 /**
  * Test that a removeKeys on an empty sessionID returns BAD_VALUE
  */
-TEST_F(DrmHalClearkeyPluginTest, RemoveKeysEmptySessionId) {
+TEST_P(DrmHalClearkeyPluginTest, RemoveKeysEmptySessionId) {
     SessionId sessionId;
     Status status = drmPlugin->removeKeys(sessionId);
     EXPECT_TRUE(status == Status::BAD_VALUE);
@@ -559,7 +576,7 @@
 /**
  * Remove keys is not supported for clearkey.
  */
-TEST_F(DrmHalClearkeyPluginTest, RemoveKeysNewSession) {
+TEST_P(DrmHalClearkeyPluginTest, RemoveKeysNewSession) {
     SessionId sessionId = openSession();
     Status status = drmPlugin->removeKeys(sessionId);
     // Clearkey plugin doesn't support remove keys
@@ -571,7 +588,7 @@
  * Test that ClearKey cannot handle key restoring.
  * Expected message is Status::ERROR_DRM_CANNOT_HANDLE.
  */
-TEST_F(DrmHalClearkeyPluginTest, RestoreKeysCannotHandle) {
+TEST_P(DrmHalClearkeyPluginTest, RestoreKeysCannotHandle) {
     hidl_vec<uint8_t> keySetId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
     SessionId sessionId = openSession();
     Status status = drmPlugin->restoreKeys(sessionId, keySetId);
@@ -583,7 +600,7 @@
  * Test that restoreKeys fails with a null key set ID.
  * Error message is expected to be Status::BAD_VALUE.
  */
-TEST_F(DrmHalClearkeyPluginTest, RestoreKeysNull) {
+TEST_P(DrmHalClearkeyPluginTest, RestoreKeysNull) {
     SessionId sessionId = openSession();
     hidl_vec<uint8_t> nullKeySetId;
     Status status = drmPlugin->restoreKeys(sessionId, nullKeySetId);
@@ -595,7 +612,7 @@
  * Test that the clearkey plugin doesn't support getting
  * secure stops.
  */
-TEST_F(DrmHalClearkeyPluginTest, GetSecureStops) {
+TEST_P(DrmHalClearkeyPluginTest, GetSecureStops) {
     auto res = drmPlugin->getSecureStops(
             [&](Status status, const hidl_vec<SecureStop>&) {
                 // Clearkey plugin doesn't support secure stops
@@ -608,7 +625,7 @@
  * Test that the clearkey plugin returns BAD_VALUE if
  * an empty ssid is provided.
  */
-TEST_F(DrmHalClearkeyPluginTest, GetSecureStopEmptySSID) {
+TEST_P(DrmHalClearkeyPluginTest, GetSecureStopEmptySSID) {
     SecureStopId ssid;
     auto res = drmPlugin->getSecureStop(
             ssid, [&](Status status, const SecureStop&) {
@@ -621,7 +638,7 @@
  * Test that releasing all secure stops isn't handled by
  * clearkey.
  */
-TEST_F(DrmHalClearkeyPluginTest, ReleaseAllSecureStops) {
+TEST_P(DrmHalClearkeyPluginTest, ReleaseAllSecureStops) {
     EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
               drmPlugin->releaseAllSecureStops());
 }
@@ -630,7 +647,7 @@
  * Test that releasing a specific secure stop with an empty
  * SSID returns BAD_VALUE.
  */
-TEST_F(DrmHalClearkeyPluginTest, ReleaseSecureStopEmptySSID) {
+TEST_P(DrmHalClearkeyPluginTest, ReleaseSecureStopEmptySSID) {
     SecureStopId ssid;
     Status status = drmPlugin->releaseSecureStop(ssid);
     EXPECT_EQ(Status::BAD_VALUE, status);
@@ -641,7 +658,7 @@
  * defined in the MediaDrm API are supported by
  * the plugin.
  */
-TEST_F(DrmHalClearkeyPluginTest, GetVendorProperty) {
+TEST_P(DrmHalClearkeyPluginTest, GetVendorProperty) {
     auto res = drmPlugin->getPropertyString(
             "vendor", [&](Status status, const hidl_string& value) {
                 EXPECT_EQ(Status::OK, status);
@@ -650,7 +667,7 @@
     EXPECT_OK(res);
 }
 
-TEST_F(DrmHalClearkeyPluginTest, GetVersionProperty) {
+TEST_P(DrmHalClearkeyPluginTest, GetVersionProperty) {
     auto res = drmPlugin->getPropertyString(
             "version", [&](Status status, const hidl_string& value) {
                 EXPECT_EQ(Status::OK, status);
@@ -659,7 +676,7 @@
     EXPECT_OK(res);
 }
 
-TEST_F(DrmHalClearkeyPluginTest, GetDescriptionProperty) {
+TEST_P(DrmHalClearkeyPluginTest, GetDescriptionProperty) {
     auto res = drmPlugin->getPropertyString(
             "description", [&](Status status, const hidl_string& value) {
                 EXPECT_EQ(Status::OK, status);
@@ -668,7 +685,7 @@
     EXPECT_OK(res);
 }
 
-TEST_F(DrmHalClearkeyPluginTest, GetAlgorithmsProperty) {
+TEST_P(DrmHalClearkeyPluginTest, GetAlgorithmsProperty) {
     auto res = drmPlugin->getPropertyString(
             "algorithms", [&](Status status, const hidl_string& value) {
                 EXPECT_EQ(Status::OK, status);
@@ -681,7 +698,7 @@
  * Test that attempting to read invalid string and byte array
  * properties returns the documented error code.
  */
-TEST_F(DrmHalClearkeyPluginTest, GetInvalidStringProperty) {
+TEST_P(DrmHalClearkeyPluginTest, GetInvalidStringProperty) {
     auto res = drmPlugin->getPropertyString(
             "invalid", [&](Status status, const hidl_string&) {
                 EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
@@ -689,7 +706,7 @@
     EXPECT_OK(res);
 }
 
-TEST_F(DrmHalClearkeyPluginTest, GetByteArrayPropertyNotSupported) {
+TEST_P(DrmHalClearkeyPluginTest, GetByteArrayPropertyNotSupported) {
     auto res = drmPlugin->getPropertyByteArray(
             "deviceUniqueId", [&](Status status, const hidl_vec<uint8_t>&) {
                 EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
@@ -701,12 +718,12 @@
  * Clearkey doesn't support setting string or byte array properties,
  * particularly an undefined one.
  */
-TEST_F(DrmHalClearkeyPluginTest, SetStringPropertyNotSupported) {
+TEST_P(DrmHalClearkeyPluginTest, SetStringPropertyNotSupported) {
     Status status = drmPlugin->setPropertyString("property", "value");
     EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
 }
 
-TEST_F(DrmHalClearkeyPluginTest, SetByteArrayPropertyNotSupported) {
+TEST_P(DrmHalClearkeyPluginTest, SetByteArrayPropertyNotSupported) {
     hidl_vec<uint8_t> value;
     Status status = drmPlugin->setPropertyByteArray("property", value);
     EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
@@ -715,7 +732,7 @@
 /**
  * Clearkey doesn't support setting cipher algorithms, verify it
  */
-TEST_F(DrmHalClearkeyPluginTest, SetCipherAlgorithmNotSupported) {
+TEST_P(DrmHalClearkeyPluginTest, SetCipherAlgorithmNotSupported) {
     SessionId session = openSession();
     hidl_string algorithm = "AES/CBC/NoPadding";
     Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
@@ -726,7 +743,7 @@
 /**
  * Setting an empty algorithm should return BAD_VALUE
  */
-TEST_F(DrmHalClearkeyPluginTest, SetCipherEmptyAlgorithm) {
+TEST_P(DrmHalClearkeyPluginTest, SetCipherEmptyAlgorithm) {
     SessionId session = openSession();
     hidl_string algorithm;
     Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
@@ -737,7 +754,7 @@
 /**
  * Setting a cipher algorithm with no session returns BAD_VALUE
  */
-TEST_F(DrmHalClearkeyPluginTest, SetCipherAlgorithmNoSession) {
+TEST_P(DrmHalClearkeyPluginTest, SetCipherAlgorithmNoSession) {
     SessionId session;
     hidl_string algorithm = "AES/CBC/NoPadding";
     Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
@@ -747,7 +764,7 @@
 /**
  * Clearkey doesn't support setting mac algorithms, verify it
  */
-TEST_F(DrmHalClearkeyPluginTest, SetMacAlgorithmNotSupported) {
+TEST_P(DrmHalClearkeyPluginTest, SetMacAlgorithmNotSupported) {
     SessionId session = openSession();
     hidl_string algorithm = "HmacSHA256";
     Status status = drmPlugin->setMacAlgorithm(session, algorithm);
@@ -758,7 +775,7 @@
 /**
  * Setting an empty algorithm should return BAD_VALUE
  */
-TEST_F(DrmHalClearkeyPluginTest, SetMacEmptyAlgorithm) {
+TEST_P(DrmHalClearkeyPluginTest, SetMacEmptyAlgorithm) {
     SessionId session = openSession();
     hidl_string algorithm;
     Status status = drmPlugin->setMacAlgorithm(session, algorithm);
@@ -769,7 +786,7 @@
 /**
  * Setting a mac algorithm with no session should return BAD_VALUE
  */
-TEST_F(DrmHalClearkeyPluginTest, SetMacAlgorithmNoSession) {
+TEST_P(DrmHalClearkeyPluginTest, SetMacAlgorithmNoSession) {
     SessionId session;
     hidl_string algorithm = "HmacSHA256";
     Status status = drmPlugin->setMacAlgorithm(session, algorithm);
@@ -786,7 +803,7 @@
  *
  * Clearkey doesn't support generic encrypt/decrypt/sign/verify.
  */
-TEST_F(DrmHalClearkeyPluginTest, GenericEncryptNotSupported) {
+TEST_P(DrmHalClearkeyPluginTest, GenericEncryptNotSupported) {
     SessionId session = openSession();
 
     hidl_vec<uint8_t> keyId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
@@ -801,7 +818,7 @@
     closeSession(session);
 }
 
-TEST_F(DrmHalClearkeyPluginTest, GenericDecryptNotSupported) {
+TEST_P(DrmHalClearkeyPluginTest, GenericDecryptNotSupported) {
     SessionId session = openSession();
     hidl_vec<uint8_t> keyId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
     hidl_vec<uint8_t> input = {1, 2, 3, 4, 5};
@@ -815,7 +832,7 @@
     closeSession(session);
 }
 
-TEST_F(DrmHalClearkeyPluginTest, GenericSignNotSupported) {
+TEST_P(DrmHalClearkeyPluginTest, GenericSignNotSupported) {
     SessionId session = openSession();
 
     hidl_vec<uint8_t> keyId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
@@ -829,7 +846,7 @@
     closeSession(session);
 }
 
-TEST_F(DrmHalClearkeyPluginTest, GenericVerifyNotSupported) {
+TEST_P(DrmHalClearkeyPluginTest, GenericVerifyNotSupported) {
     SessionId session = openSession();
 
     hidl_vec<uint8_t> keyId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
@@ -844,7 +861,7 @@
     closeSession(session);
 }
 
-TEST_F(DrmHalClearkeyPluginTest, GenericSignRSANotSupported) {
+TEST_P(DrmHalClearkeyPluginTest, GenericSignRSANotSupported) {
     SessionId session = openSession();
     hidl_string algorithm = "RSASSA-PSS-SHA1";
     hidl_vec<uint8_t> message = {1, 2, 3, 4, 5};
@@ -867,14 +884,14 @@
  * Clearkey doesn't support secure decoder and is expected to
  * return false.
  */
-TEST_F(DrmHalClearkeyPluginTest, RequiresSecureDecoder) {
+TEST_P(DrmHalClearkeyPluginTest, RequiresSecureDecoder) {
     EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent("cenc"));
 }
 
 /**
  * Verify that requiresSecureDecoderComponent handles empty mimetype
  */
-TEST_F(DrmHalClearkeyPluginTest, RequiresSecureDecoderEmptyMimeType) {
+TEST_P(DrmHalClearkeyPluginTest, RequiresSecureDecoderEmptyMimeType) {
     EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent(""));
 }
 
@@ -882,7 +899,7 @@
  * Exercise the NotifyResolution API. There is no observable result,
  * just call the method for coverage.
  */
-TEST_F(DrmHalClearkeyPluginTest, NotifyResolution) {
+TEST_P(DrmHalClearkeyPluginTest, NotifyResolution) {
     cryptoPlugin->notifyResolution(1920, 1080);
 }
 
@@ -919,7 +936,7 @@
  * Exercise the setMediaDrmSession method. setMediaDrmSession
  * is used to associate a drm session with a crypto session.
  */
-TEST_F(DrmHalClearkeyPluginTest, SetMediaDrmSession) {
+TEST_P(DrmHalClearkeyPluginTest, SetMediaDrmSession) {
     auto sessionId = openSession();
     EXPECT_TRUE(cryptoPlugin->setMediaDrmSession(sessionId).isOk());
     closeSession(sessionId);
@@ -928,7 +945,7 @@
 /**
  * setMediaDrmSession with a closed session id
  */
-TEST_F(DrmHalClearkeyPluginTest, SetMediaDrmSessionClosedSession) {
+TEST_P(DrmHalClearkeyPluginTest, SetMediaDrmSessionClosedSession) {
     auto sessionId = openSession();
     closeSession(sessionId);
     Status status = cryptoPlugin->setMediaDrmSession(sessionId);
@@ -940,7 +957,7 @@
  * empty session clears the previously set session and should
  * return OK.
  */
-TEST_F(DrmHalClearkeyPluginTest, SetMediaDrmSessionEmptySession) {
+TEST_P(DrmHalClearkeyPluginTest, SetMediaDrmSessionEmptySession) {
     SessionId sessionId;
     EXPECT_TRUE(cryptoPlugin->setMediaDrmSession(sessionId).isOk());
 }
@@ -951,6 +968,13 @@
 
 class DrmHalClearkeyDecryptTest : public DrmHalClearkeyPluginTest {
    public:
+     void SetUp() override {
+         DrmHalClearkeyPluginTest::SetUp();
+
+         if (!correspondsToThisTest) {
+             GTEST_SKIP() << "Cannot test clearkey features on non-clearkey DRM modules";
+         }
+     }
     void fillRandom(const sp<IMemory>& memory);
     hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) {
         EXPECT_EQ(16u, vec.size());
@@ -1109,7 +1133,7 @@
 /**
  * Test query key status
  */
-TEST_F(DrmHalClearkeyDecryptTest, TestQueryKeyStatus) {
+TEST_P(DrmHalClearkeyDecryptTest, TestQueryKeyStatus) {
     auto sessionId = openSession();
     auto res = drmPlugin->queryKeyStatus(
         sessionId, [&](Status status, KeyedVector /* info */) { EXPECT_EQ(Status::OK, status); });
@@ -1121,7 +1145,7 @@
 /**
  * Positive decrypt test.  "Decrypt" a single clear segment
  */
-TEST_F(DrmHalClearkeyDecryptTest, ClearSegmentTest) {
+TEST_P(DrmHalClearkeyDecryptTest, ClearSegmentTest) {
     vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
     const Pattern noPattern = {0, 0};
     const uint32_t kByteCount = 256;
@@ -1143,7 +1167,7 @@
  * Positive decrypt test.  Decrypt a single segment using AES_CTR.
  * Verify data matches.
  */
-TEST_F(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTest) {
+TEST_P(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTest) {
     vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
     const Pattern noPattern = {0, 0};
     const uint32_t kClearBytes = 512;
@@ -1165,7 +1189,7 @@
 /**
  * Negative decrypt test. Decrypt without loading keys.
  */
-TEST_F(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTestNoKeys) {
+TEST_P(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTestNoKeys) {
     vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
     const Pattern noPattern = {0, 0};
     const vector<SubSample> subSamples = {
@@ -1211,7 +1235,7 @@
 /**
  * Negative decrypt test. Decrypt with invalid key.
  */
-TEST_F(DrmHalClearkeyDecryptTest, DecryptWithEmptyKey) {
+TEST_P(DrmHalClearkeyDecryptTest, DecryptWithEmptyKey) {
     vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
     const Pattern noPattern = {0, 0};
     const uint32_t kClearBytes = 512;
@@ -1248,7 +1272,7 @@
 /**
  * Negative decrypt test. Decrypt with a key exceeds AES_BLOCK_SIZE.
  */
-TEST_F(DrmHalClearkeyDecryptTest, DecryptWithKeyTooLong) {
+TEST_P(DrmHalClearkeyDecryptTest, DecryptWithKeyTooLong) {
     vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
     const Pattern noPattern = {0, 0};
     const uint32_t kClearBytes = 512;
@@ -1275,3 +1299,21 @@
     memcpy(invalidResponse.data(), keyTooLongResponse.c_str(), kKeyTooLongResponseSize);
     decryptWithInvalidKeys(invalidResponse, iv, noPattern, subSamples);
 }
+
+static const std::set<std::string> kAllInstances = [] {
+    std::vector<std::string> drmInstances =
+            android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor);
+    std::vector<std::string> cryptoInstances =
+            android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor);
+    std::set<std::string> allInstances;
+    allInstances.insert(drmInstances.begin(), drmInstances.end());
+    allInstances.insert(cryptoInstances.begin(), cryptoInstances.end());
+    return allInstances;
+}();
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyFactoryTest, testing::ValuesIn(kAllInstances),
+                         android::hardware::PrintInstanceNameToString);
+INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyPluginTest, testing::ValuesIn(kAllInstances),
+                         android::hardware::PrintInstanceNameToString);
+INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyDecryptTest, testing::ValuesIn(kAllInstances),
+                         android::hardware::PrintInstanceNameToString);
diff --git a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
index 20a2ca4..2259985 100644
--- a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
+++ b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
@@ -24,16 +24,17 @@
 #include <android/hardware/drm/1.0/types.h>
 #include <android/hidl/allocator/1.0/IAllocator.h>
 #include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
 #include <hidlmemory/mapping.h>
 #include <log/log.h>
-#include <memory>
 #include <openssl/aes.h>
+#include <memory>
 #include <random>
 
 #include "drm_hal_vendor_module_api.h"
 #include "vendor_modules.h"
 #include <VtsHalHidlTargetCallbackBase.h>
-#include <VtsHalHidlTargetTestBase.h>
 
 using ::android::hardware::drm::V1_0::BufferType;
 using ::android::hardware::drm::V1_0::DestinationBuffer;
@@ -77,17 +78,17 @@
 
 using ContentConfiguration = ::DrmHalVTSVendorModule_V1::ContentConfiguration;
 using Key = ::DrmHalVTSVendorModule_V1::ContentConfiguration::Key;
-using VtsTestBase = ::testing::VtsHalHidlTargetTestBase;
 
 #define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
 #define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
 
-#define RETURN_IF_SKIPPED \
-    if (!vendorModule->isInstalled()) { \
-        std::cout << "[  SKIPPED ] This drm scheme not supported." << \
-                " library:" << GetParam() << " service-name:" << \
-                vendorModule->getServiceName() << std::endl; \
-        return; \
+#define RETURN_IF_SKIPPED                                                                  \
+    if (vendorModule == nullptr || !vendorModule->isInstalled()) {                         \
+        GTEST_SKIP() << "This drm scheme not supported."                                   \
+                     << " library:" << GetParam() << " service-name:"                      \
+                     << (vendorModule == nullptr ? "N/A" : vendorModule->getServiceName()) \
+                     << std::endl;                                                         \
+        return;                                                                            \
     }
 
 static const uint8_t kInvalidUUID[16] = {
@@ -97,73 +98,47 @@
 
 static drm_vts::VendorModules* gVendorModules = nullptr;
 
-// Test environment for drm
-class DrmHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
-   public:
-    // get the test environment singleton
-    static DrmHidlEnvironment* Instance() {
-        static DrmHidlEnvironment* instance = new DrmHidlEnvironment;
-        return instance;
-    }
-
-    void registerTestServices() override {
-        registerTestService<ICryptoFactory>();
-        registerTestService<IDrmFactory>();
-        setServiceCombMode(::testing::HalServiceCombMode::NO_COMBINATION);
-    }
-
-   private:
-    DrmHidlEnvironment() {}
-
-    GTEST_DISALLOW_COPY_AND_ASSIGN_(DrmHidlEnvironment);
-};
-
 class DrmHalVendorFactoryTest : public testing::TestWithParam<std::string> {
    public:
-    DrmHalVendorFactoryTest()
-        : vendorModule(static_cast<DrmHalVTSVendorModule_V1*>(
-                        gVendorModules->getModule(GetParam()))),
-          contentConfigurations(vendorModule->getContentConfigurations()) {}
+     DrmHalVendorFactoryTest()
+         : vendorModule(
+                   static_cast<DrmHalVTSVendorModule_V1*>(gVendorModules->getModule(GetParam()))) {}
 
-    virtual ~DrmHalVendorFactoryTest() {}
+     virtual ~DrmHalVendorFactoryTest() {}
 
-    virtual void SetUp() {
-        const ::testing::TestInfo* const test_info =
-                ::testing::UnitTest::GetInstance()->current_test_info();
-        ALOGD("Running test %s.%s from vendor module %s",
-              test_info->test_case_name(), test_info->name(),
-              GetParam().c_str());
+     virtual void SetUp() {
+         const ::testing::TestInfo* const test_info =
+                 ::testing::UnitTest::GetInstance()->current_test_info();
+         ALOGD("Running test %s.%s from vendor module %s", test_info->test_case_name(),
+               test_info->name(), GetParam().c_str());
 
-        ASSERT_NE(nullptr, vendorModule.get());
+         const std::string instance = GetParam();
+         if (instance == "widevine") {
+             ASSERT_NE(nullptr, vendorModule.get());
+         }
 
-        // First try the binderized service name provided by the vendor module.
-        // If that fails, which it can on non-binderized devices, try the default
-        // service.
-        string name = vendorModule->getServiceName();
-        drmFactory = VtsTestBase::getService<IDrmFactory>(name);
-        if (drmFactory == nullptr) {
-            drmFactory = VtsTestBase::getService<IDrmFactory>();
-        }
-        ASSERT_NE(nullptr, drmFactory.get());
+         if (vendorModule == nullptr) {
+             GTEST_SKIP() << "No vendor module available";
+         } else {
+             ASSERT_EQ(instance, vendorModule->getServiceName());
+             contentConfigurations = vendorModule->getContentConfigurations();
+         }
 
-        // Do the same for the crypto factory
-        cryptoFactory = VtsTestBase::getService<ICryptoFactory>(name);
-        if (cryptoFactory == nullptr) {
-            cryptoFactory = VtsTestBase::getService<ICryptoFactory>();
-        }
-        ASSERT_NE(nullptr, cryptoFactory.get());
+         drmFactory = IDrmFactory::getService(instance);
+         ASSERT_NE(nullptr, drmFactory.get());
+         cryptoFactory = ICryptoFactory::getService(instance);
+         ASSERT_NE(nullptr, cryptoFactory.get());
 
-        // If drm scheme not installed skip subsequent tests
-        if (!drmFactory->isCryptoSchemeSupported(getVendorUUID())) {
-            vendorModule->setInstalled(false);
-            return;
-        }
-    }
-
-    virtual void TearDown() override {}
+         // If drm scheme not installed skip subsequent tests
+         if (!drmFactory->isCryptoSchemeSupported(getVendorUUID())) {
+             // no GTEST_SKIP since only some tests require the module
+             vendorModule->setInstalled(false);
+         }
+     }
 
    protected:
     hidl_array<uint8_t, 16> getVendorUUID() {
+        if (vendorModule == nullptr) return {};
         vector<uint8_t> uuid = vendorModule->getUUID();
         return hidl_array<uint8_t, 16>(&uuid[0]);
     }
@@ -171,7 +146,7 @@
     sp<IDrmFactory> drmFactory;
     sp<ICryptoFactory> cryptoFactory;
     unique_ptr<DrmHalVTSVendorModule_V1> vendorModule;
-    const vector<ContentConfiguration> contentConfigurations;
+    vector<ContentConfiguration> contentConfigurations;
 };
 
 TEST_P(DrmHalVendorFactoryTest, ValidateConfigurations) {
@@ -1596,17 +1571,28 @@
  * Instantiate the set of test cases for each vendor module
  */
 
-INSTANTIATE_TEST_CASE_P(
-        DrmHalVendorFactoryTestCases, DrmHalVendorFactoryTest,
-        testing::ValuesIn(gVendorModules->getPathList()));
+static const std::set<std::string> kAllInstances = [] {
+    std::vector<std::string> drmInstances =
+            android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor);
+    std::vector<std::string> cryptoInstances =
+            android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor);
+    std::set<std::string> allInstances;
+    allInstances.insert(drmInstances.begin(), drmInstances.end());
+    allInstances.insert(cryptoInstances.begin(), cryptoInstances.end());
+    return allInstances;
+}();
 
-INSTANTIATE_TEST_CASE_P(
-        DrmHalVendorPluginTestCases, DrmHalVendorPluginTest,
-        testing::ValuesIn(gVendorModules->getPathList()));
+INSTANTIATE_TEST_CASE_P(DrmHalVendorFactoryTestCases, DrmHalVendorFactoryTest,
+                        testing::ValuesIn(kAllInstances),
+                        android::hardware::PrintInstanceNameToString);
 
-INSTANTIATE_TEST_CASE_P(
-        DrmHalVendorDecryptTestCases, DrmHalVendorDecryptTest,
-        testing::ValuesIn(gVendorModules->getPathList()));
+INSTANTIATE_TEST_CASE_P(DrmHalVendorPluginTestCases, DrmHalVendorPluginTest,
+                        testing::ValuesIn(kAllInstances),
+                        android::hardware::PrintInstanceNameToString);
+
+INSTANTIATE_TEST_CASE_P(DrmHalVendorDecryptTestCases, DrmHalVendorDecryptTest,
+                        testing::ValuesIn(kAllInstances),
+                        android::hardware::PrintInstanceNameToString);
 
 int main(int argc, char** argv) {
 #if defined(__LP64__)
@@ -1619,9 +1605,7 @@
         std::cerr << "WARNING: No vendor modules found in " << kModulePath <<
                 ", all vendor tests will be skipped" << std::endl;
     }
-    ::testing::AddGlobalTestEnvironment(DrmHidlEnvironment::Instance());
     ::testing::InitGoogleTest(&argc, argv);
-    DrmHidlEnvironment::Instance()->init(&argc, argv);
     int status = RUN_ALL_TESTS();
     ALOGI("Test result = %d", status);
     return status;
diff --git a/drm/1.1/vts/functional/Android.bp b/drm/1.1/vts/functional/Android.bp
index 47b02bf..fb09563 100644
--- a/drm/1.1/vts/functional/Android.bp
+++ b/drm/1.1/vts/functional/Android.bp
@@ -18,7 +18,7 @@
     name: "VtsHalDrmV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
     srcs: [
-        "drm_hal_clearkey_test.cpp"
+        "drm_hal_clearkey_test.cpp",
     ],
     static_libs: [
         "android.hardware.drm@1.0",
@@ -30,5 +30,8 @@
         "libnativehelper",
         "libssl",
     ],
-    test_suites: ["general-tests"],
+    test_suites: [
+        "general-tests",
+        "vts-core",
+    ],
 }
diff --git a/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp b/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp
index 6be30d3..bf99ef6 100644
--- a/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp
+++ b/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp
@@ -16,27 +16,25 @@
 
 #define LOG_TAG "drm_hal_clearkey_test@1.1"
 
-#include <android/hardware/drm/1.1/ICryptoFactory.h>
 #include <android/hardware/drm/1.0/ICryptoPlugin.h>
-#include <android/hardware/drm/1.1/IDrmFactory.h>
 #include <android/hardware/drm/1.0/IDrmPlugin.h>
-#include <android/hardware/drm/1.1/IDrmPlugin.h>
 #include <android/hardware/drm/1.0/types.h>
+#include <android/hardware/drm/1.1/ICryptoFactory.h>
+#include <android/hardware/drm/1.1/IDrmFactory.h>
+#include <android/hardware/drm/1.1/IDrmPlugin.h>
 #include <android/hardware/drm/1.1/types.h>
 #include <android/hidl/allocator/1.0/IAllocator.h>
 #include <android/hidl/manager/1.2/IServiceManager.h>
 #include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
 #include <hidl/HidlSupport.h>
 #include <hidl/ServiceManagement.h>
 #include <hidlmemory/mapping.h>
 #include <log/log.h>
-#include <memory>
 #include <openssl/aes.h>
+#include <memory>
 #include <random>
 
-#include "VtsHalHidlTargetTestBase.h"
-#include "VtsHalHidlTargetTestEnvBase.h"
-
 namespace drm = ::android::hardware::drm;
 using ::android::hardware::drm::V1_0::BufferType;
 using ::android::hardware::drm::V1_0::DestinationBuffer;
@@ -94,76 +92,31 @@
     0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9,
     0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E};
 
-// Test environment for drm
-class DrmHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
-   public:
-    // get the test environment singleton
-    static DrmHidlEnvironment* Instance() {
-        static DrmHidlEnvironment* instance = new DrmHidlEnvironment;
-        return instance;
-    }
-
-    virtual void HidlSetUp() override { ALOGI("SetUp DrmHidlEnvironment"); }
-
-    virtual void HidlTearDown() override { ALOGI("TearDown DrmHidlEnvironment"); }
-
-    void registerTestServices() override {
-        registerTestService<ICryptoFactory>();
-        registerTestService<IDrmFactory>();
-        setServiceCombMode(::testing::HalServiceCombMode::NO_COMBINATION);
-    }
-
-   private:
-    DrmHidlEnvironment() {}
-
-    GTEST_DISALLOW_COPY_AND_ASSIGN_(DrmHidlEnvironment);
-};
-
-
-class DrmHalClearkeyTest : public ::testing::VtsHalHidlTargetTestBase {
-public:
-    virtual void SetUp() override {
+class DrmHalClearkeyTest : public ::testing::TestWithParam<std::string> {
+  public:
+    void SetUp() override {
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
 
         ALOGD("DrmHalClearkeyTest: Running test %s.%s", test_info->test_case_name(),
                 test_info->name());
 
-        auto manager = android::hardware::defaultServiceManager1_2();
-        ASSERT_NE(nullptr, manager.get());
-        manager->listManifestByInterface(IDrmFactory::descriptor,
-                [&](const hidl_vec<hidl_string> &registered) {
-                    for (const auto &instance : registered) {
-                        sp<IDrmFactory> drmFactory =
-                                ::testing::VtsHalHidlTargetTestBase::getService<IDrmFactory>(instance);
-                        drmPlugin = createDrmPlugin(drmFactory);
-                        if (drmPlugin != nullptr) {
-                            break;
-                        }
-                    }
-                }
-            );
+        const std::string instance = GetParam();
 
-        manager->listManifestByInterface(ICryptoFactory::descriptor,
-                [&](const hidl_vec<hidl_string> &registered) {
-                    for (const auto &instance : registered) {
-                        sp<ICryptoFactory> cryptoFactory =
-                                ::testing::VtsHalHidlTargetTestBase::getService<ICryptoFactory>(instance);
-                        cryptoPlugin = createCryptoPlugin(cryptoFactory);
-                        if (cryptoPlugin != nullptr) {
-                            break;
-                        }
-                    }
-                }
-            );
+        sp<IDrmFactory> drmFactory = IDrmFactory::getService(instance);
+        drmPlugin = createDrmPlugin(drmFactory);
+        sp<ICryptoFactory> cryptoFactory = ICryptoFactory::getService(instance);
+        cryptoPlugin = createCryptoPlugin(cryptoFactory);
 
-        ASSERT_NE(nullptr, drmPlugin.get()) << "Can't find clearkey drm@1.1 plugin";
-        ASSERT_NE(nullptr, cryptoPlugin.get()) << "Can't find clearkey crypto@1.1 plugin";
+        if (drmPlugin == nullptr || cryptoPlugin == nullptr) {
+            if (instance == "clearkey") {
+                ASSERT_NE(nullptr, drmPlugin.get()) << "Can't get clearkey drm@1.1 plugin";
+                ASSERT_NE(nullptr, cryptoPlugin.get()) << "Can't get clearkey crypto@1.1 plugin";
+            }
+            GTEST_SKIP() << "Instance does not support clearkey";
+        }
     }
 
-
-    virtual void TearDown() override {}
-
     SessionId openSession();
     SessionId openSession(SecurityLevel level);
     void closeSession(const SessionId& sessionId);
@@ -176,9 +129,8 @@
         }
         sp<IDrmPlugin> plugin = nullptr;
         auto res = drmFactory->createPlugin(
-                kClearKeyUUID, "",
-                        [&](Status status, const sp<drm::V1_0::IDrmPlugin>& pluginV1_0) {
-                    EXPECT_EQ(Status::OK, status);
+                kClearKeyUUID, "", [&](Status status, const sp<drm::V1_0::IDrmPlugin>& pluginV1_0) {
+                    EXPECT_EQ(Status::OK == status, pluginV1_0 != nullptr);
                     plugin = IDrmPlugin::castFrom(pluginV1_0);
                 });
 
@@ -196,8 +148,8 @@
         hidl_vec<uint8_t> initVec;
         auto res = cryptoFactory->createPlugin(
                 kClearKeyUUID, initVec,
-                        [&](Status status, const sp<drm::V1_0::ICryptoPlugin>& pluginV1_0) {
-                    EXPECT_EQ(Status::OK, status);
+                [&](Status status, const sp<drm::V1_0::ICryptoPlugin>& pluginV1_0) {
+                    EXPECT_EQ(Status::OK == status, pluginV1_0 != nullptr);
                     plugin = pluginV1_0;
                 });
         if (!res.isOk()) {
@@ -265,7 +217,6 @@
  sp<ICryptoPlugin> cryptoPlugin;
 };
 
-
 /**
  * Helper method to open a session and verify that a non-empty
  * session ID is returned
@@ -371,7 +322,7 @@
 /**
  * Test openSession negative case: security level higher than supported
  */
-TEST_F(DrmHalClearkeyTest, OpenSessionBadLevel) {
+TEST_P(DrmHalClearkeyTest, OpenSessionBadLevel) {
     auto res = drmPlugin->openSession_1_1(SecurityLevel::HW_SECURE_ALL,
             [&](Status status, const SessionId& /* id */) {
                 EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
@@ -382,7 +333,7 @@
 /**
  * Test getKeyRequest_1_1 via loadKeys
  */
-TEST_F(DrmHalClearkeyTest, GetKeyRequest) {
+TEST_P(DrmHalClearkeyTest, GetKeyRequest) {
     auto sessionId = openSession();
     loadKeys(sessionId);
     closeSession(sessionId);
@@ -391,7 +342,7 @@
 /**
  * A get key request should fail if no sessionId is provided
  */
-TEST_F(DrmHalClearkeyTest, GetKeyRequestNoSession) {
+TEST_P(DrmHalClearkeyTest, GetKeyRequestNoSession) {
     SessionId invalidSessionId;
     hidl_vec<uint8_t> initData;
     hidl_string mimeType = "video/mp4";
@@ -409,7 +360,7 @@
  * Test that the plugin returns the expected error code in
  * this case.
  */
-TEST_F(DrmHalClearkeyTest, GetKeyRequestOfflineKeyTypeNotSupported) {
+TEST_P(DrmHalClearkeyTest, GetKeyRequestOfflineKeyTypeNotSupported) {
     auto sessionId = openSession();
     hidl_vec<uint8_t> initData;
     hidl_string mimeType = "video/mp4";
@@ -429,7 +380,7 @@
 /**
  * Test that the plugin returns valid connected and max HDCP levels
  */
-TEST_F(DrmHalClearkeyTest, GetHdcpLevels) {
+TEST_P(DrmHalClearkeyTest, GetHdcpLevels) {
     auto res = drmPlugin->getHdcpLevels(
             [&](Status status, const HdcpLevel &connectedLevel,
                 const HdcpLevel &maxLevel) {
@@ -448,7 +399,7 @@
 /**
  * Test that the plugin returns default open and max session counts
  */
-TEST_F(DrmHalClearkeyTest, GetDefaultSessionCounts) {
+TEST_P(DrmHalClearkeyTest, GetDefaultSessionCounts) {
     auto res = drmPlugin->getNumberOfSessions(
             [&](Status status, uint32_t currentSessions,
                     uint32_t maxSessions) {
@@ -464,7 +415,7 @@
  * Test that the plugin returns valid open and max session counts
  * after a session is opened.
  */
-TEST_F(DrmHalClearkeyTest, GetOpenSessionCounts) {
+TEST_P(DrmHalClearkeyTest, GetOpenSessionCounts) {
     uint32_t initialSessions = 0;
     auto res = drmPlugin->getNumberOfSessions(
             [&](Status status, uint32_t currentSessions,
@@ -505,7 +456,7 @@
  * Test that the plugin returns the same security level
  * by default as when it is requested explicitly
  */
-TEST_F(DrmHalClearkeyTest, GetDefaultSecurityLevel) {
+TEST_P(DrmHalClearkeyTest, GetDefaultSecurityLevel) {
     SessionId session = openSession();
     SecurityLevel defaultLevel;
     auto res = drmPlugin->getSecurityLevel(session,
@@ -530,7 +481,7 @@
  * Test that the plugin returns the lowest security level
  * when it is requested
  */
-TEST_F(DrmHalClearkeyTest, GetSecurityLevel) {
+TEST_P(DrmHalClearkeyTest, GetSecurityLevel) {
     SessionId session = openSession(SecurityLevel::SW_SECURE_CRYPTO);
     auto res = drmPlugin->getSecurityLevel(session,
             [&](Status status, SecurityLevel level) {
@@ -545,7 +496,7 @@
  * Test that the plugin returns the documented error
  * when requesting the security level for an invalid sessionId
  */
-TEST_F(DrmHalClearkeyTest, GetSecurityLevelInvalidSessionId) {
+TEST_P(DrmHalClearkeyTest, GetSecurityLevelInvalidSessionId) {
     SessionId session;
     auto res = drmPlugin->getSecurityLevel(session,
             [&](Status status, SecurityLevel /*level*/) {
@@ -557,7 +508,7 @@
 /**
  * Test metrics are set appropriately for open and close operations.
  */
-TEST_F(DrmHalClearkeyTest, GetMetricsOpenClose) {
+TEST_P(DrmHalClearkeyTest, GetMetricsOpenClose) {
     SessionId sessionId = openSession();
     // The first close should be successful.
     closeSession(sessionId);
@@ -589,7 +540,7 @@
 /**
  * Test that there are no secure stop ids after clearing them
  */
-TEST_F(DrmHalClearkeyTest, GetSecureStopIdsCleared) {
+TEST_P(DrmHalClearkeyTest, GetSecureStopIdsCleared) {
     auto stat = drmPlugin->removeAllSecureStops();
     EXPECT_OK(stat);
 
@@ -604,7 +555,7 @@
 /**
  * Test that there are secure stop ids after loading keys once
  */
-TEST_F(DrmHalClearkeyTest, GetSecureStopIdsOnce) {
+TEST_P(DrmHalClearkeyTest, GetSecureStopIdsOnce) {
     auto stat = drmPlugin->removeAllSecureStops();
     EXPECT_OK(stat);
 
@@ -639,7 +590,7 @@
  * Test that the clearkey plugin reports no secure stops when
  * there are none.
  */
-TEST_F(DrmHalClearkeyTest, GetNoSecureStops) {
+TEST_P(DrmHalClearkeyTest, GetNoSecureStops) {
     auto stat = drmPlugin->removeAllSecureStops();
     EXPECT_OK(stat);
 
@@ -654,7 +605,7 @@
 /**
  * Test get/remove of one secure stop
  */
-TEST_F(DrmHalClearkeyTest, GetOneSecureStopAndRemoveIt) {
+TEST_P(DrmHalClearkeyTest, GetOneSecureStopAndRemoveIt) {
     auto stat = drmPlugin->removeAllSecureStops();
     EXPECT_OK(stat);
 
@@ -688,7 +639,7 @@
 /**
  * Test that there are no secure stops after clearing them
  */
-TEST_F(DrmHalClearkeyTest, GetSecureStopsCleared) {
+TEST_P(DrmHalClearkeyTest, GetSecureStopsCleared) {
     auto stat = drmPlugin->removeAllSecureStops();
     EXPECT_OK(stat);
 
@@ -703,7 +654,7 @@
 /**
  * Test that there are secure stops after loading keys once
  */
-TEST_F(DrmHalClearkeyTest, GetSecureStopsOnce) {
+TEST_P(DrmHalClearkeyTest, GetSecureStopsOnce) {
     auto stat = drmPlugin->removeAllSecureStops();
     EXPECT_OK(stat);
 
@@ -738,7 +689,7 @@
  * Test that releasing a secure stop with empty
  * release message fails with the documented error
  */
-TEST_F(DrmHalClearkeyTest, ReleaseEmptySecureStop) {
+TEST_P(DrmHalClearkeyTest, ReleaseEmptySecureStop) {
     SecureStopRelease emptyRelease = {.opaqueData = hidl_vec<uint8_t>()};
     Status status = drmPlugin->releaseSecureStops(emptyRelease);
     EXPECT_EQ(Status::BAD_VALUE, status);
@@ -763,8 +714,7 @@
 /**
  * Test that releasing one secure stop works
  */
-TEST_F(DrmHalClearkeyTest, ReleaseOneSecureStop) {
-
+TEST_P(DrmHalClearkeyTest, ReleaseOneSecureStop) {
     auto stat = drmPlugin->removeAllSecureStops();
     EXPECT_OK(stat);
 
@@ -792,12 +742,11 @@
     EXPECT_OK(res);
 }
 
-
 /**
  * Test that removing a secure stop with an empty ID returns
  * documented error
  */
-TEST_F(DrmHalClearkeyTest, RemoveEmptySecureStopId) {
+TEST_P(DrmHalClearkeyTest, RemoveEmptySecureStopId) {
     hidl_vec<uint8_t> emptyId;
     auto stat = drmPlugin->removeSecureStop(emptyId);
     EXPECT_OK(stat);
@@ -808,7 +757,7 @@
  * Test that removing a secure stop after it has already
  * been removed fails with the documented error code.
  */
-TEST_F(DrmHalClearkeyTest, RemoveRemovedSecureStopId) {
+TEST_P(DrmHalClearkeyTest, RemoveRemovedSecureStopId) {
     auto stat = drmPlugin->removeAllSecureStops();
     EXPECT_OK(stat);
 
@@ -835,7 +784,7 @@
 /**
  * Test that removing a secure stop by id works
  */
-TEST_F(DrmHalClearkeyTest, RemoveSecureStopById) {
+TEST_P(DrmHalClearkeyTest, RemoveSecureStopById) {
     auto stat = drmPlugin->removeAllSecureStops();
     EXPECT_OK(stat);
 
@@ -863,12 +812,16 @@
     EXPECT_OK(res);
 }
 
+static const std::set<std::string> kAllInstances = [] {
+    std::vector<std::string> drmInstances =
+            android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor);
+    std::vector<std::string> cryptoInstances =
+            android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor);
+    std::set<std::string> allInstances;
+    allInstances.insert(drmInstances.begin(), drmInstances.end());
+    allInstances.insert(cryptoInstances.begin(), cryptoInstances.end());
+    return allInstances;
+}();
 
-int main(int argc, char** argv) {
-    ::testing::AddGlobalTestEnvironment(DrmHidlEnvironment::Instance());
-    ::testing::InitGoogleTest(&argc, argv);
-    DrmHidlEnvironment::Instance()->init(&argc, argv);
-    int status = RUN_ALL_TESTS();
-    ALOGI("Test result = %d", status);
-    return status;
-}
+INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyTest, testing::ValuesIn(kAllInstances),
+                         android::hardware::PrintInstanceNameToString);
diff --git a/drm/1.2/vts/functional/Android.bp b/drm/1.2/vts/functional/Android.bp
index 95883bf..83ea104 100644
--- a/drm/1.2/vts/functional/Android.bp
+++ b/drm/1.2/vts/functional/Android.bp
@@ -17,13 +17,13 @@
 cc_test {
     name: "VtsHalDrmV1_2TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
+    include_dirs: ["hardware/interfaces/drm/1.0/vts/functional"],
     srcs: [
         "drm_hal_clearkey_module.cpp",
         "drm_hal_common.cpp",
         "drm_hal_test.cpp",
         "vendor_modules.cpp",
     ],
-    include_dirs: ["hardware/interfaces/drm/1.0/vts/functional"],
     static_libs: [
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.1",
@@ -36,5 +36,8 @@
         "libssl",
         "libcrypto_static",
     ],
-    test_suites: ["general-tests"],
+    test_suites: [
+        "general-tests",
+        "vts-core",
+    ],
 }
diff --git a/drm/1.2/vts/functional/drm_hal_common.cpp b/drm/1.2/vts/functional/drm_hal_common.cpp
index 71e29ec..b169268 100644
--- a/drm/1.2/vts/functional/drm_hal_common.cpp
+++ b/drm/1.2/vts/functional/drm_hal_common.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "vendor_modules.h"
 #define LOG_TAG "drm_hal_common@1.2"
 
 #include <android/hidl/allocator/1.0/IAllocator.h>
@@ -81,16 +82,19 @@
     return Void();
 }
 
+static DrmHalVTSVendorModule_V1* getModuleForInstance(const std::string& instance) {
+    if (instance == "clearkey" || instance == "default") {
+        return new DrmHalVTSClearkeyModule();
+    }
+
+    return static_cast<DrmHalVTSVendorModule_V1*>(DrmHalTest::gVendorModules->getModuleByName(instance));
+}
+
 /**
  * DrmHalTest
  */
 
-DrmHalTest::DrmHalTest()
-    : vendorModule(GetParam() == "clearkey"
-            ? new DrmHalVTSClearkeyModule()
-            : static_cast<DrmHalVTSVendorModule_V1*>(gVendorModules->getModule(GetParam()))),
-      contentConfigurations(vendorModule->getContentConfigurations()) {
-}
+DrmHalTest::DrmHalTest() : vendorModule(getModuleForInstance(GetParam())) {}
 
 void DrmHalTest::SetUp() {
     const ::testing::TestInfo* const test_info =
@@ -100,27 +104,28 @@
           test_info->test_case_name(), test_info->name(),
           GetParam().c_str());
 
-    string name = vendorModule->getServiceName();
-    drmFactory = VtsHalHidlTargetTestBase::getService<IDrmFactory>(name);
-    if (drmFactory == nullptr) {
-        drmFactory = VtsHalHidlTargetTestBase::getService<IDrmFactory>();
-    }
-    if (drmFactory != nullptr) {
-        drmPlugin = createDrmPlugin();
+    const string instance = GetParam();
+
+    drmFactory = IDrmFactory::getService(instance);
+    ASSERT_NE(drmFactory, nullptr);
+    drmPlugin = createDrmPlugin();
+
+    cryptoFactory = ICryptoFactory::getService(instance);
+    ASSERT_NE(cryptoFactory, nullptr);
+    cryptoPlugin = createCryptoPlugin();
+
+    if (!vendorModule) {
+        ASSERT_NE(instance, "widevine") << "Widevine requires vendor module.";
+        ASSERT_NE(instance, "clearkey") << "Clearkey requires vendor module.";
+        GTEST_SKIP() << "No vendor module installed";
     }
 
-    cryptoFactory = VtsHalHidlTargetTestBase::getService<ICryptoFactory>(name);
-    if (cryptoFactory == nullptr) {
-        cryptoFactory = VtsHalHidlTargetTestBase::getService<ICryptoFactory>();
-    }
-    if (cryptoFactory != nullptr) {
-        cryptoPlugin = createCryptoPlugin();
-    }
+    ASSERT_EQ(instance, vendorModule->getServiceName());
+    contentConfigurations = vendorModule->getContentConfigurations();
 
     // If drm scheme not installed skip subsequent tests
-    if (drmFactory.get() == nullptr || !drmFactory->isCryptoSchemeSupported(getVendorUUID())) {
-        vendorModule->setInstalled(false);
-        return;
+    if (!drmFactory->isCryptoSchemeSupported(getVendorUUID())) {
+        GTEST_SKIP() << "vendor module drm scheme not supported";
     }
 
     ASSERT_NE(nullptr, drmPlugin.get()) << "Can't find " << vendorModule->getServiceName() <<  " drm@1.2 plugin";
@@ -134,12 +139,12 @@
     }
     sp<IDrmPlugin> plugin = nullptr;
     hidl_string packageName("android.hardware.drm.test");
-    auto res = drmFactory->createPlugin(
-            getVendorUUID(), packageName,
-                    [&](StatusV1_0 status, const sp<IDrmPluginV1_0>& pluginV1_0) {
-                EXPECT_EQ(StatusV1_0::OK, status);
-                plugin = IDrmPlugin::castFrom(pluginV1_0);
-            });
+    auto res =
+            drmFactory->createPlugin(getVendorUUID(), packageName,
+                                     [&](StatusV1_0 status, const sp<IDrmPluginV1_0>& pluginV1_0) {
+                                         EXPECT_EQ(StatusV1_0::OK == status, pluginV1_0 != nullptr);
+                                         plugin = IDrmPlugin::castFrom(pluginV1_0);
+                                     });
 
     if (!res.isOk()) {
         ALOGE("createDrmPlugin remote call failed");
@@ -155,8 +160,8 @@
     hidl_vec<uint8_t> initVec;
     auto res = cryptoFactory->createPlugin(
             getVendorUUID(), initVec,
-                    [&](StatusV1_0 status, const sp<ICryptoPluginV1_0>& pluginV1_0) {
-                EXPECT_EQ(StatusV1_0::OK, status);
+            [&](StatusV1_0 status, const sp<ICryptoPluginV1_0>& pluginV1_0) {
+                EXPECT_EQ(StatusV1_0::OK == status, pluginV1_0 != nullptr);
                 plugin = ICryptoPlugin::castFrom(pluginV1_0);
             });
     if (!res.isOk()) {
@@ -166,6 +171,7 @@
 }
 
 hidl_array<uint8_t, 16> DrmHalTest::getVendorUUID() {
+    if (vendorModule == nullptr) return {};
     vector<uint8_t> uuid = vendorModule->getUUID();
     return hidl_array<uint8_t, 16>(&uuid[0]);
 }
diff --git a/drm/1.2/vts/functional/drm_hal_common.h b/drm/1.2/vts/functional/drm_hal_common.h
index e348664..03b1b87 100644
--- a/drm/1.2/vts/functional/drm_hal_common.h
+++ b/drm/1.2/vts/functional/drm_hal_common.h
@@ -33,7 +33,6 @@
 #include "drm_hal_vendor_module_api.h"
 #include "vendor_modules.h"
 #include "VtsHalHidlTargetCallbackBase.h"
-#include "VtsHalHidlTargetTestBase.h"
 
 using ::android::hardware::drm::V1_0::EventType;
 using ::android::hardware::drm::V1_0::KeyedVector;
@@ -56,8 +55,6 @@
 using ::android::hidl::memory::V1_0::IMemory;
 using ::android::sp;
 
-using ::testing::VtsHalHidlTargetTestBase;
-
 using std::map;
 using std::string;
 using std::unique_ptr;
@@ -65,40 +62,12 @@
 
 #define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
 
-#define RETURN_IF_SKIPPED \
-    if (!vendorModule->isInstalled()) { \
-        std::cout << "[  SKIPPED ] This drm scheme not supported." << \
-                " library:" << GetParam() << " service-name:" << \
-                vendorModule->getServiceName() << std::endl; \
-        return; \
-    }
-
 namespace android {
 namespace hardware {
 namespace drm {
 namespace V1_2 {
 namespace vts {
 
-class DrmHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
-   public:
-    // get the test environment singleton
-    static DrmHidlEnvironment* Instance() {
-        static DrmHidlEnvironment* instance = new DrmHidlEnvironment;
-        return instance;
-    }
-
-    void registerTestServices() override {
-        registerTestService<ICryptoFactory>();
-        registerTestService<IDrmFactory>();
-        setServiceCombMode(::testing::HalServiceCombMode::NO_COMBINATION);
-    }
-
-   private:
-    DrmHidlEnvironment() {}
-
-    GTEST_DISALLOW_COPY_AND_ASSIGN_(DrmHidlEnvironment);
-};
-
 class DrmHalTest : public ::testing::TestWithParam<std::string> {
    public:
     static drm_vts::VendorModules* gVendorModules;
@@ -142,9 +111,9 @@
     sp<IDrmPlugin> drmPlugin;
     sp<ICryptoPlugin> cryptoPlugin;
     unique_ptr<DrmHalVTSVendorModule_V1> vendorModule;
-    const vector<DrmHalVTSVendorModule_V1::ContentConfiguration> contentConfigurations;
+    vector<DrmHalVTSVendorModule_V1::ContentConfiguration> contentConfigurations;
 
-   private:
+  private:
     sp<IDrmPlugin> createDrmPlugin();
     sp<ICryptoPlugin> createCryptoPlugin();
 
@@ -152,7 +121,15 @@
 
 class DrmHalClearkeyTest : public DrmHalTest {
    public:
-    virtual void SetUp() override { DrmHalTest::SetUp(); }
+     virtual void SetUp() override {
+         DrmHalTest::SetUp();
+         const uint8_t kClearKeyUUID[16] = {
+             0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9,
+             0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E};
+         if (!drmFactory->isCryptoSchemeSupported(kClearKeyUUID)) {
+             GTEST_SKIP() << "ClearKey not supported by " << GetParam();
+         }
+     }
     virtual void TearDown() override {}
     void decryptWithInvalidKeys(hidl_vec<uint8_t>& invalidResponse,
             vector<uint8_t>& iv, const Pattern& noPattern, const vector<SubSample>& subSamples);
diff --git a/drm/1.2/vts/functional/drm_hal_test.cpp b/drm/1.2/vts/functional/drm_hal_test.cpp
index 5e1dfed..48becc1 100644
--- a/drm/1.2/vts/functional/drm_hal_test.cpp
+++ b/drm/1.2/vts/functional/drm_hal_test.cpp
@@ -17,7 +17,9 @@
 #define LOG_TAG "drm_hal_test@1.2"
 
 #include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
 #include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
 #include <log/log.h>
 #include <openssl/aes.h>
 
@@ -35,7 +37,6 @@
 using ::android::hardware::drm::V1_2::vts::DrmHalClearkeyTest;
 using ::android::hardware::drm::V1_2::vts::DrmHalPluginListener;
 using ::android::hardware::drm::V1_2::vts::DrmHalTest;
-using ::android::hardware::drm::V1_2::vts::DrmHidlEnvironment;
 using ::android::hardware::drm::V1_2::vts::kCallbackLostState;
 using ::android::hardware::drm::V1_2::vts::kCallbackKeysChange;
 
@@ -52,7 +53,6 @@
  * Ensure drm factory supports module UUID Scheme
  */
 TEST_P(DrmHalTest, VendorUuidSupported) {
-    RETURN_IF_SKIPPED;
     auto res = drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kVideoMp4, kSwSecureCrypto);
     ALOGI("kVideoMp4 = %s res %d", kVideoMp4, (bool)res);
     EXPECT_TRUE(res);
@@ -62,7 +62,6 @@
  * Ensure drm factory doesn't support an invalid scheme UUID
  */
 TEST_P(DrmHalTest, InvalidPluginNotSupported) {
-    RETURN_IF_SKIPPED;
     const uint8_t kInvalidUUID[16] = {
         0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
         0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
@@ -73,7 +72,6 @@
  * Ensure drm factory doesn't support an empty UUID
  */
 TEST_P(DrmHalTest, EmptyPluginUUIDNotSupported) {
-    RETURN_IF_SKIPPED;
     hidl_array<uint8_t, 16> emptyUUID;
     memset(emptyUUID.data(), 0, 16);
     EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(emptyUUID, kVideoMp4, kSwSecureCrypto));
@@ -83,7 +81,6 @@
  * Ensure drm factory doesn't support an invalid mime type
  */
 TEST_P(DrmHalTest, BadMimeNotSupported) {
-    RETURN_IF_SKIPPED;
     EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kBadMime, kSwSecureCrypto));
 }
 
@@ -100,7 +97,6 @@
  * that is delivered back to the HAL.
  */
 TEST_P(DrmHalTest, DoProvisioning) {
-    RETURN_IF_SKIPPED;
     hidl_string certificateType;
     hidl_string certificateAuthority;
     hidl_vec<uint8_t> provisionRequest;
@@ -137,7 +133,6 @@
  * A get key request should fail if no sessionId is provided
  */
 TEST_P(DrmHalTest, GetKeyRequestNoSession) {
-    RETURN_IF_SKIPPED;
     SessionId invalidSessionId;
     hidl_vec<uint8_t> initData;
     KeyedVector optionalParameters;
@@ -155,7 +150,6 @@
  * invalid mime type
  */
 TEST_P(DrmHalTest, GetKeyRequestBadMime) {
-    RETURN_IF_SKIPPED;
     auto sessionId = openSession();
     hidl_vec<uint8_t> initData;
     KeyedVector optionalParameters;
@@ -192,7 +186,6 @@
  * Test drm plugin offline key support
  */
 TEST_P(DrmHalTest, OfflineLicenseTest) {
-    RETURN_IF_SKIPPED;
     auto sessionId = openSession();
     hidl_vec<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE);
 
@@ -232,7 +225,6 @@
  * Test drm plugin offline key state
  */
 TEST_P(DrmHalTest, OfflineLicenseStateTest) {
-    RETURN_IF_SKIPPED;
     auto sessionId = openSession();
     DrmHalVTSVendorModule_V1::ContentConfiguration content = getContent(KeyType::OFFLINE);
     hidl_vec<uint8_t> keySetId = loadKeys(sessionId, content, KeyType::OFFLINE);
@@ -257,7 +249,6 @@
  * Negative offline license test. Remove empty keySetId
  */
 TEST_P(DrmHalTest, RemoveEmptyKeySetId) {
-    RETURN_IF_SKIPPED;
     KeySetId emptyKeySetId;
     Status err = drmPlugin->removeOfflineLicense(emptyKeySetId);
     EXPECT_EQ(Status::BAD_VALUE, err);
@@ -267,7 +258,6 @@
  * Negative offline license test. Get empty keySetId state
  */
 TEST_P(DrmHalTest, GetEmptyKeySetIdState) {
-    RETURN_IF_SKIPPED;
     KeySetId emptyKeySetId;
     auto res = drmPlugin->getOfflineLicenseState(emptyKeySetId, checkKeySetIdState<Status::BAD_VALUE, OfflineLicenseState::UNKNOWN>);
     EXPECT_OK(res);
@@ -277,7 +267,6 @@
  * Test that the plugin returns valid connected and max HDCP levels
  */
 TEST_P(DrmHalTest, GetHdcpLevels) {
-    RETURN_IF_SKIPPED;
     auto res = drmPlugin->getHdcpLevels_1_2(
             [&](StatusV1_2 status, const HdcpLevel &connectedLevel,
                 const HdcpLevel &maxLevel) {
@@ -293,7 +282,6 @@
  * the listener gets them.
  */
 TEST_P(DrmHalTest, ListenerKeysChange) {
-    RETURN_IF_SKIPPED;
     sp<DrmHalPluginListener> listener = new DrmHalPluginListener();
     auto res = drmPlugin->setListener(listener);
     EXPECT_OK(res);
@@ -325,7 +313,6 @@
  * Positive decrypt test.  "Decrypt" a single clear segment
  */
 TEST_P(DrmHalTest, ClearSegmentTest) {
-    RETURN_IF_SKIPPED;
     for (const auto& config : contentConfigurations) {
         for (const auto& key : config.keys) {
             const size_t kSegmentSize = 1024;
@@ -353,7 +340,6 @@
  * Verify data matches.
  */
 TEST_P(DrmHalTest, EncryptedAesCtrSegmentTest) {
-    RETURN_IF_SKIPPED;
     for (const auto& config : contentConfigurations) {
         for (const auto& key : config.keys) {
             const size_t kSegmentSize = 1024;
@@ -380,7 +366,6 @@
  * Negative decrypt test.  Decrypted frame too large to fit in output buffer
  */
 TEST_P(DrmHalTest, ErrorFrameTooLarge) {
-    RETURN_IF_SKIPPED;
     for (const auto& config : contentConfigurations) {
         for (const auto& key : config.keys) {
             const size_t kSegmentSize = 1024;
@@ -406,7 +391,6 @@
  * Negative decrypt test. Decrypt without loading keys.
  */
 TEST_P(DrmHalTest, EncryptedAesCtrSegmentTestNoKeys) {
-    RETURN_IF_SKIPPED;
     for (const auto& config : contentConfigurations) {
         for (const auto& key : config.keys) {
             vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
@@ -432,7 +416,6 @@
  * Ensure clearkey drm factory doesn't support security level higher than supported
  */
 TEST_P(DrmHalClearkeyTest, BadLevelNotSupported) {
-    RETURN_IF_SKIPPED;
     const SecurityLevel kHwSecureAll = SecurityLevel::HW_SECURE_ALL;
     EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kVideoMp4, kHwSecureAll));
 }
@@ -441,7 +424,6 @@
  * Test resource contention during attempt to generate key request
  */
 TEST_P(DrmHalClearkeyTest, GetKeyRequestResourceContention) {
-    RETURN_IF_SKIPPED;
     Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorResourceContention);
     EXPECT_EQ(Status::OK, status);
     auto sessionId = openSession();
@@ -463,7 +445,6 @@
  * Test clearkey plugin offline key with mock error
  */
 TEST_P(DrmHalClearkeyTest, OfflineLicenseInvalidState) {
-    RETURN_IF_SKIPPED;
     auto sessionId = openSession();
     hidl_vec<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE);
     Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorInvalidState);
@@ -485,7 +466,6 @@
  * Test SessionLostState is triggered on error
  */
 TEST_P(DrmHalClearkeyTest, SessionLostState) {
-    RETURN_IF_SKIPPED;
     sp<DrmHalPluginListener> listener = new DrmHalPluginListener();
     auto res = drmPlugin->setListener(listener);
     EXPECT_OK(res);
@@ -506,7 +486,6 @@
  * Negative decrypt test. Decrypt with invalid key.
  */
 TEST_P(DrmHalClearkeyTest, DecryptWithEmptyKey) {
-    RETURN_IF_SKIPPED;
     vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
     const Pattern noPattern = {0, 0};
     const uint32_t kClearBytes = 512;
@@ -544,7 +523,6 @@
  * Negative decrypt test. Decrypt with a key exceeds AES_BLOCK_SIZE.
  */
 TEST_P(DrmHalClearkeyTest, DecryptWithKeyTooLong) {
-    RETURN_IF_SKIPPED;
     vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
     const Pattern noPattern = {0, 0};
     const uint32_t kClearBytes = 512;
@@ -576,17 +554,24 @@
  * Instantiate the set of test cases for each vendor module
  */
 
-INSTANTIATE_TEST_CASE_P(
-        DrmHalTestClearkey, DrmHalTest,
-        testing::Values("clearkey"));
+static const std::set<std::string> kAllInstances = [] {
+    using ::android::hardware::drm::V1_2::ICryptoFactory;
+    using ::android::hardware::drm::V1_2::IDrmFactory;
 
-INSTANTIATE_TEST_CASE_P(
-        DrmHalTestClearkeyExtended, DrmHalClearkeyTest,
-        testing::Values("clearkey"));
+    std::vector<std::string> drmInstances =
+            android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor);
+    std::vector<std::string> cryptoInstances =
+            android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor);
+    std::set<std::string> allInstances;
+    allInstances.insert(drmInstances.begin(), drmInstances.end());
+    allInstances.insert(cryptoInstances.begin(), cryptoInstances.end());
+    return allInstances;
+}();
 
-INSTANTIATE_TEST_CASE_P(
-        DrmHalTestVendor, DrmHalTest,
-        testing::ValuesIn(DrmHalTest::gVendorModules->getPathList()));
+INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalTest, testing::ValuesIn(kAllInstances),
+                         android::hardware::PrintInstanceNameToString);
+INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyTest, testing::ValuesIn(kAllInstances),
+                         android::hardware::PrintInstanceNameToString);
 
 int main(int argc, char** argv) {
 #if defined(__LP64__)
@@ -599,9 +584,7 @@
         std::cerr << "WARNING: No vendor modules found in " << kModulePath <<
                 ", all vendor tests will be skipped" << std::endl;
     }
-    ::testing::AddGlobalTestEnvironment(DrmHidlEnvironment::Instance());
     ::testing::InitGoogleTest(&argc, argv);
-    DrmHidlEnvironment::Instance()->init(&argc, argv);
     int status = RUN_ALL_TESTS();
     ALOGI("Test result = %d", status);
     return status;
diff --git a/drm/1.2/vts/functional/vendor_modules.cpp b/drm/1.2/vts/functional/vendor_modules.cpp
index efcb90a..08131cf 100644
--- a/drm/1.2/vts/functional/vendor_modules.cpp
+++ b/drm/1.2/vts/functional/vendor_modules.cpp
@@ -23,6 +23,7 @@
 #include <utils/String8.h>
 #include <SharedLibrary.h>
 
+#include "drm_hal_vendor_module_api.h"
 #include "vendor_modules.h"
 
 using std::string;
@@ -69,4 +70,14 @@
     ModuleFactory moduleFactory = reinterpret_cast<ModuleFactory>(symbol);
     return (*moduleFactory)();
 }
+
+DrmHalVTSVendorModule* VendorModules::getModuleByName(const string& name) {
+    for (const auto &path : mPathList) {
+        auto module = getModule(path);
+        if (module->getServiceName() == name) {
+            return module;
+        }
+    }
+    return NULL;
+}
 };
diff --git a/drm/1.2/vts/functional/vendor_modules.h b/drm/1.2/vts/functional/vendor_modules.h
index 9b730ad..eacd2d1 100644
--- a/drm/1.2/vts/functional/vendor_modules.h
+++ b/drm/1.2/vts/functional/vendor_modules.h
@@ -47,6 +47,11 @@
     DrmHalVTSVendorModule* getModule(const std::string& path);
 
     /**
+     * Retrieve a DrmHalVTSVendorModule given a service name.
+     */
+    DrmHalVTSVendorModule* getModuleByName(const std::string& name);
+
+    /**
      * Return the list of paths to available vendor modules.
      */
     std::vector<std::string> getPathList() const {return mPathList;}
diff --git a/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp b/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp
index 96b13c5..343d4c9 100644
--- a/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp
+++ b/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp
@@ -78,6 +78,7 @@
     ASSERT_EQ(1, read(fds[0], &buff, 1)) << "dumped nothing";
 
     native_handle_close(handle);
+    native_handle_delete(handle);
 }
 
 // Positive test: make sure dumpstateBoard() doesn't crash with two FDs.
@@ -96,6 +97,7 @@
     ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
 
     native_handle_close(handle);
+    native_handle_delete(handle);
 }
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/dumpstate/1.1/Android.bp b/dumpstate/1.1/Android.bp
new file mode 100644
index 0000000..2aa8c82
--- /dev/null
+++ b/dumpstate/1.1/Android.bp
@@ -0,0 +1,18 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.dumpstate@1.1",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "types.hal",
+        "IDumpstateDevice.hal",
+    ],
+    interfaces: [
+        "android.hardware.dumpstate@1.0",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+}
diff --git a/dumpstate/1.1/IDumpstateDevice.hal b/dumpstate/1.1/IDumpstateDevice.hal
new file mode 100644
index 0000000..24831b3
--- /dev/null
+++ b/dumpstate/1.1/IDumpstateDevice.hal
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.dumpstate@1.1;
+
+import @1.0::IDumpstateDevice;
+
+interface IDumpstateDevice extends @1.0::IDumpstateDevice {
+    /**
+     * Extension of dumpstateBoard which also accepts a mode parameter to limit dumped data.
+     *
+     * For an example of when this is relevant, consider a bug report being generated with
+     * DumpstateMode::CONNECTIVITY - there is no reason to include camera or USB logs in this type
+     * of report.
+     *
+     * The 1.0 version of #dumpstateBoard(handle) should just delegate to this new method and pass
+     * DumpstateMode::DEFAULT and a timeout of 30,000ms (30 seconds).
+     *
+     * @param h A native handle with one or two valid file descriptors. The first FD is for text
+     *     output, the second (if present) is for binary output.
+     * @param mode A mode value to restrict dumped content.
+     * @param timeoutMillis An approximate "budget" for how much time this call has been allotted.
+     *     If execution runs longer than this, the IDumpstateDevice service may be killed and only
+     *     partial information will be included in the report.
+     */
+    dumpstateBoard_1_1(handle h, DumpstateMode mode, uint64_t timeoutMillis);
+
+    /**
+     * Turns device vendor logging on or off.
+     *
+     * The setting should be persistent across reboots. Underlying implementations may need to start
+     * vendor logging daemons, set system properties, or change logging masks, for example. Given
+     * that many vendor logs contain significant amounts of private information and may come with
+     * memory/storage/battery impacts, calling this method on a user build should only be done after
+     * user consent has been obtained, e.g. from a toggle in developer settings.
+     *
+     * @param enable Whether to enable or disable device vendor logging.
+     * @return success Whether or not the change took effect.
+     */
+    setDeviceLoggingEnabled(bool enable) generates (bool success);
+};
diff --git a/dumpstate/1.1/types.hal b/dumpstate/1.1/types.hal
new file mode 100644
index 0000000..a6f391a
--- /dev/null
+++ b/dumpstate/1.1/types.hal
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.dumpstate@1.1;
+
+/**
+ * Constants that define the type of bug report being taken to restrict content appropriately.
+ */
+enum DumpstateMode : uint32_t {
+    /**
+     * Takes a bug report without user interference.
+     */
+    FULL = 0,
+
+    /**
+     * Interactive bug report, i.e. triggered by the user.
+     */
+    INTERACTIVE = 1,
+
+    /**
+     * Remote bug report triggered by DevicePolicyManager, for example.
+     */
+    REMOTE = 2,
+
+    /**
+     * Bug report triggered on a wear device.
+     */
+    WEAR = 3,
+
+    /**
+     * Bug report limited to only connectivity info (cellular, wifi, and networking). Sometimes
+     * called "telephony" in legacy contexts.
+     *
+     * All reported information MUST directly relate to connectivity debugging or customer support
+     * and MUST NOT contain unrelated private information. This information MUST NOT identify
+     * user-installed packages (UIDs are OK, package names are not), and MUST NOT contain logs of
+     * user application traffic.
+     */
+    CONNECTIVITY = 4,
+
+    /**
+     * Bug report limited to only wifi info.
+     */
+    WIFI = 5,
+
+    /**
+     * Default mode.
+     */
+    DEFAULT = 6
+};
diff --git a/dumpstate/1.1/vts/functional/Android.bp b/dumpstate/1.1/vts/functional/Android.bp
new file mode 100644
index 0000000..5267706
--- /dev/null
+++ b/dumpstate/1.1/vts/functional/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "VtsHalDumpstateV1_1TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalDumpstateV1_1TargetTest.cpp"],
+    static_libs: [
+        "android.hardware.dumpstate@1.0",
+        "android.hardware.dumpstate@1.1",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts-core",
+    ],
+}
diff --git a/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp b/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
new file mode 100644
index 0000000..3b6051c
--- /dev/null
+++ b/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate_1_1_hidl_hal_test"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <vector>
+
+#include <android/hardware/dumpstate/1.1/IDumpstateDevice.h>
+#include <android/hardware/dumpstate/1.1/types.h>
+#include <cutils/native_handle.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hardware::dumpstate::V1_1::DumpstateMode;
+using ::android::hardware::dumpstate::V1_1::IDumpstateDevice;
+
+class DumpstateHidl1_1Test : public ::testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        dumpstate = IDumpstateDevice::getService(GetParam());
+        ASSERT_NE(dumpstate, nullptr) << "Could not get HIDL instance";
+    }
+
+    sp<IDumpstateDevice> dumpstate;
+};
+
+#define TEST_FOR_DUMPSTATE_MODE(name, body, mode) \
+    TEST_P(DumpstateHidl1_1Test, name##_##mode) { body(DumpstateMode::mode); }
+
+#define TEST_FOR_ALL_DUMPSTATE_MODES(name, body)       \
+    TEST_FOR_DUMPSTATE_MODE(name, body, FULL);         \
+    TEST_FOR_DUMPSTATE_MODE(name, body, INTERACTIVE);  \
+    TEST_FOR_DUMPSTATE_MODE(name, body, REMOTE);       \
+    TEST_FOR_DUMPSTATE_MODE(name, body, WEAR);         \
+    TEST_FOR_DUMPSTATE_MODE(name, body, CONNECTIVITY); \
+    TEST_FOR_DUMPSTATE_MODE(name, body, WIFI);         \
+    TEST_FOR_DUMPSTATE_MODE(name, body, DEFAULT);
+
+const uint64_t kDefaultTimeoutMillis = 30 * 1000;  // 30 seconds
+
+// Negative test: make sure dumpstateBoard() doesn't crash when passed a null pointer.
+TEST_FOR_ALL_DUMPSTATE_MODES(TestNullHandle, [this](DumpstateMode mode) {
+    Return<void> status = dumpstate->dumpstateBoard_1_1(nullptr, mode, kDefaultTimeoutMillis);
+
+    ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+});
+
+// Negative test: make sure dumpstateBoard() ignores a handle with no FD.
+TEST_FOR_ALL_DUMPSTATE_MODES(TestHandleWithNoFd, [this](DumpstateMode mode) {
+    native_handle_t* handle = native_handle_create(0, 0);
+    ASSERT_NE(handle, nullptr) << "Could not create native_handle";
+
+    Return<void> status = dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+
+    ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+
+    native_handle_close(handle);
+    native_handle_delete(handle);
+});
+
+// Positive test: make sure dumpstateBoard() writes something to the FD.
+TEST_FOR_ALL_DUMPSTATE_MODES(TestOk, [this](DumpstateMode mode) {
+    // Index 0 corresponds to the read end of the pipe; 1 to the write end.
+    int fds[2];
+    ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno;
+
+    native_handle_t* handle = native_handle_create(1, 0);
+    ASSERT_NE(handle, nullptr) << "Could not create native_handle";
+    handle->data[0] = fds[1];
+
+    Return<void> status = dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+    ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+
+    // Check that at least one byte was written
+    char buff;
+    ASSERT_EQ(1, read(fds[0], &buff, 1)) << "dumped nothing";
+
+    native_handle_close(handle);
+    native_handle_delete(handle);
+});
+
+// Positive test: make sure dumpstateBoard() doesn't crash with two FDs.
+TEST_FOR_ALL_DUMPSTATE_MODES(TestHandleWithTwoFds, [this](DumpstateMode mode) {
+    int fds1[2];
+    int fds2[2];
+    ASSERT_EQ(0, pipe2(fds1, O_NONBLOCK)) << errno;
+    ASSERT_EQ(0, pipe2(fds2, O_NONBLOCK)) << errno;
+
+    native_handle_t* handle = native_handle_create(2, 0);
+    ASSERT_NE(handle, nullptr) << "Could not create native_handle";
+    handle->data[0] = fds1[1];
+    handle->data[1] = fds2[1];
+
+    Return<void> status = dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+    ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+
+    native_handle_close(handle);
+    native_handle_delete(handle);
+});
+
+// Make sure dumpstateBoard_1_1 actually validates its arguments.
+TEST_P(DumpstateHidl1_1Test, TestInvalidModeArgument_Negative) {
+    int fds[2];
+    ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno;
+
+    native_handle_t* handle = native_handle_create(1, 0);
+    ASSERT_NE(handle, nullptr) << "Could not create native_handle";
+    handle->data[0] = fds[1];
+
+    Return<void> status = dumpstate->dumpstateBoard_1_1(handle, static_cast<DumpstateMode>(-100),
+                                                        kDefaultTimeoutMillis);
+    ASSERT_FALSE(status.isOk()) << "Status should not be ok with invalid mode param: "
+                                << status.description();
+
+    native_handle_close(handle);
+    native_handle_delete(handle);
+}
+
+TEST_P(DumpstateHidl1_1Test, TestInvalidModeArgument_Undefined) {
+    int fds[2];
+    ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno;
+
+    native_handle_t* handle = native_handle_create(1, 0);
+    ASSERT_NE(handle, nullptr) << "Could not create native_handle";
+    handle->data[0] = fds[1];
+
+    Return<void> status = dumpstate->dumpstateBoard_1_1(handle, static_cast<DumpstateMode>(9001),
+                                                        kDefaultTimeoutMillis);
+    ASSERT_FALSE(status.isOk()) << "Status should not be ok with invalid mode param: "
+                                << status.description();
+
+    native_handle_close(handle);
+    native_handle_delete(handle);
+}
+
+// Make sure toggling device logging doesn't crash.
+TEST_P(DumpstateHidl1_1Test, TestEnableDeviceLogging) {
+    Return<bool> status = dumpstate->setDeviceLoggingEnabled(true);
+
+    ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+}
+
+TEST_P(DumpstateHidl1_1Test, TestDisableDeviceLogging) {
+    Return<bool> status = dumpstate->setDeviceLoggingEnabled(false);
+
+    ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, DumpstateHidl1_1Test,
+        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IDumpstateDevice::descriptor)),
+        android::hardware::PrintInstanceNameToString);
diff --git a/gnss/1.0/IGnssCallback.hal b/gnss/1.0/IGnssCallback.hal
index d62676f..311ab21 100644
--- a/gnss/1.0/IGnssCallback.hal
+++ b/gnss/1.0/IGnssCallback.hal
@@ -90,6 +90,7 @@
          * - QZSS:    193-200
          * - Galileo: 1-36
          * - Beidou:  1-37
+         * - IRNSS:   1-14
          */
         int16_t svid;
 
diff --git a/gnss/2.1/Android.bp b/gnss/2.1/Android.bp
index 8b0c374..c615f1d 100644
--- a/gnss/2.1/Android.bp
+++ b/gnss/2.1/Android.bp
@@ -7,6 +7,7 @@
         enabled: true,
     },
     srcs: [
+        "types.hal",
         "IGnss.hal",
         "IGnssCallback.hal",
         "IGnssMeasurement.hal",
diff --git a/gnss/2.1/IGnssMeasurementCallback.hal b/gnss/2.1/IGnssMeasurementCallback.hal
index ca6175f..0385abd 100644
--- a/gnss/2.1/IGnssMeasurementCallback.hal
+++ b/gnss/2.1/IGnssMeasurementCallback.hal
@@ -17,24 +17,115 @@
 package android.hardware.gnss@2.1;
 
 import @1.0::IGnssMeasurementCallback;
-import @1.1::IGnssMeasurementCallback;
 import @2.0::IGnssMeasurementCallback;
 import @2.0::ElapsedRealtime;
+import GnssSignalType;
 
 /** The callback interface to report measurements from the HAL. */
 interface IGnssMeasurementCallback extends @2.0::IGnssMeasurementCallback {
 
     /**
-     * Extends a GNSS Measurement, adding a basebandCN0DbHz.
+     * Flags to indicate what fields in GnssMeasurement are valid.
+     */
+    enum GnssMeasurementFlags : uint32_t {
+        /** A valid 'snr' is stored in the data structure. */
+        HAS_SNR                           = 1 << 0,
+        /** A valid 'carrier frequency' is stored in the data structure. */
+        HAS_CARRIER_FREQUENCY             = 1 << 9,
+        /** A valid 'carrier cycles' is stored in the data structure. */
+        HAS_CARRIER_CYCLES                = 1 << 10,
+        /** A valid 'carrier phase' is stored in the data structure. */
+        HAS_CARRIER_PHASE                 = 1 << 11,
+        /** A valid 'carrier phase uncertainty' is stored in the data structure. */
+        HAS_CARRIER_PHASE_UNCERTAINTY     = 1 << 12,
+        /** A valid automatic gain control is stored in the data structure. */
+        HAS_AUTOMATIC_GAIN_CONTROL        = 1 << 13,
+        /** A valid receiver inter-signal bias is stored in the data structure. */
+        HAS_RECEIVER_ISB                  = 1 << 16,
+        /** A valid receiver inter-signal bias uncertainty is stored in the data structure. */
+        HAS_RECEIVER_ISB_UNCERTAINTY      = 1 << 17,
+        /** A valid satellite inter-signal bias is stored in the data structure. */
+        HAS_SATELLITE_ISB                 = 1 << 18,
+        /** A valid satellite inter-signal bias uncertainty is stored in the data structure. */
+        HAS_SATELLITE_ISB_UNCERTAINTY     = 1 << 19
+    };
+
+
+    /**
+     * Extends a GNSS Measurement, adding basebandCN0DbHz, GnssMeasurementFlags,
+     * receiverInterSignalBiasNs, receiverInterSignalBiasUncertaintyNs, satelliteInterSignalBiasNs
+     * and satelliteInterSignalBiasUncertaintyNs.
      */
     struct GnssMeasurement {
         /**
          * GNSS measurement information for a single satellite and frequency, as in the 2.0 version
          * of the HAL.
+         *
+         * In this version of the HAL, the field 'flags' in the v2_0.v1_1.v1_0 struct is deprecated,
+         * and is no longer used by the framework. The GNSS measurement flags are instead reported
+         * in @2.1::IGnssMeasurementCallback.GnssMeasurement.flags.
+         *
          */
         @2.0::IGnssMeasurementCallback.GnssMeasurement v2_0;
 
         /**
+         * A set of flags indicating the validity of the fields in this data
+         * structure.
+         *
+         * Fields for which there is no corresponding flag must be filled in
+         * with a valid value.  For convenience, these are marked as mandatory.
+         *
+         * Others fields may have invalid information in them, if not marked as
+         * valid by the corresponding bit in flags.
+         */
+        bitfield<GnssMeasurementFlags> flags;
+
+        /**
+         * The receiver inter-signal bias (ISB) in nanoseconds.
+         *
+         * This value is the estimated receiver-side inter-system (different from the constellation
+         * in GnssClock.referenceSignalForIsb) bias and inter-frequency (different from the carrier
+         * frequency in GnssClock.referenceSignalForIsb) bias. The reported receiver ISB
+         * must include signal delays caused by
+         *
+         * - Receiver inter-constellation bias
+         * - Receiver inter-frequency bias
+         * - Receiver inter-code bias
+         *
+         * The value does not include the inter-frequency Ionospheric bias.
+         *
+         * The receiver ISB of GnssClock.referenceSignalForIsb is defined to be 0.0 nanoseconds.
+         */
+        double receiverInterSignalBiasNs;
+
+        /**
+         * 1-sigma uncertainty associated with the receiver inter-signal bias in nanoseconds.
+         */
+        double receiverInterSignalBiasUncertaintyNs;
+
+        /**
+         * The satellite inter-signal bias in nanoseconds.
+         *
+         * This value is the satellite-and-control-segment-side inter-system (different from the
+         * constellation in GnssClock.referenceSignalForIsb) bias and inter-frequency (different
+         * from the carrier frequency in GnssClock.referenceSignalForIsb) bias, including:
+         *
+         * - Master clock bias (e.g., GPS-GAL Time Offset (GGTO), GPT-UTC Time Offset (TauGps),
+         *   BDS-GLO Time Offset (BGTO))
+         * - Group delay (e.g., Total Group Delay (TGD))
+         * - Satellite inter-signal bias, which includes satellite inter-frequency bias (GLO only),
+         *   and satellite inter-code bias (e.g., Differential Code Bias (DCB)).
+         *
+         * The receiver ISB of GnssClock.referenceSignalForIsb is defined to be 0.0 nanoseconds.
+         */
+        double satelliteInterSignalBiasNs;
+
+        /**
+         * 1-sigma uncertainty associated with the satellite inter-signal bias in nanoseconds.
+         */
+        double satelliteInterSignalBiasUncertaintyNs;
+
+        /**
          * Baseband Carrier-to-noise density in dB-Hz, typically in the range [0, 63]. It contains
          * the measured C/N0 value for the signal measured at the baseband.
          *
@@ -51,8 +142,22 @@
     };
 
     /**
-     * Complete set of GNSS Measurement data, same as 2.0 with additional double (i.e.,
-     * basebandCN0DbHz) in measurements.
+     * Extends a GNSS clock time, adding a referenceSignalTypeForIsb.
+     */
+    struct GnssClock {
+        /**
+         * GNSS clock time information, as in the 1.0 version of the HAL.
+         */
+        @1.0::IGnssMeasurementCallback.GnssClock v1_0;
+
+        /**
+         * Reference GNSS signal type for inter-signal bias.
+         */
+        GnssSignalType referenceSignalTypeForIsb;
+    };
+
+    /**
+     * Complete set of GNSS Measurement data, same as 2.0 with additional fields in measurements.
      */
     struct GnssData {
         /** The full set of satellite measurement observations. */
diff --git a/gnss/2.1/types.hal b/gnss/2.1/types.hal
new file mode 100644
index 0000000..e4484c1
--- /dev/null
+++ b/gnss/2.1/types.hal
@@ -0,0 +1,46 @@
+/*
+ * 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.gnss@2.1;
+
+import @2.0::GnssConstellationType;
+
+/**
+ * Represents a GNSS signal type.
+ */
+struct GnssSignalType {
+    /**
+     * Constellation type of the SV that transmits the signal.
+     */
+    GnssConstellationType constellation;
+
+    /**
+     * Carrier frequency in Hz of the signal.
+     */
+    double carrierFrequencyHz;
+
+    /**
+     * The type of code of the GNSS signal.
+     *
+     * This is used to specify the observation descriptor defined in GNSS Observation Data File
+     * Header Section Description in the RINEX standard (Version 3.XX). In RINEX Version 3.03,
+     * in Appendix Table A2 Attributes are listed as uppercase letters (for instance, "A" for
+     * "A channel").
+     *
+     * See the comment of @2.0::IGnssMeasurementCallback.GnssMeasurement.codeType for more details.
+     */
+    string codeType;
+};
diff --git a/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
index 2c51717..9a7bd77 100644
--- a/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "GnssHalTestCases"
 
 #include <gnss_hal_test.h>
+#include <cmath>
 #include "Utils.h"
 
 #include <gtest/gtest.h>
@@ -30,6 +31,7 @@
 using IGnssMeasurement_2_0 = android::hardware::gnss::V2_0::IGnssMeasurement;
 using IGnssMeasurement_1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
 using IGnssMeasurement_1_0 = android::hardware::gnss::V1_0::IGnssMeasurement;
+
 using IGnssConfiguration_2_1 = android::hardware::gnss::V2_1::IGnssConfiguration;
 using IGnssConfiguration_2_0 = android::hardware::gnss::V2_0::IGnssConfiguration;
 using IGnssConfiguration_1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
@@ -38,6 +40,8 @@
 using android::hardware::gnss::V2_0::GnssConstellationType;
 using android::hardware::gnss::V2_1::IGnssConfiguration;
 
+using GnssMeasurementFlags = IGnssMeasurementCallback_2_1::GnssMeasurementFlags;
+
 /*
  * SetupTeardownCreateCleanup:
  * Requests the gnss HAL then calls cleanup
@@ -92,6 +96,7 @@
  * TestGnssMeasurementFields:
  * Sets a GnssMeasurementCallback, waits for a measurement, and verifies
  * 1. basebandCN0DbHz is valid
+ * 2. ISB fields are valid if HAS_INTER_SIGNAL_BIAS is true.
  */
 TEST_P(GnssHalTest, TestGnssMeasurementFields) {
     const int kFirstGnssMeasurementTimeoutSeconds = 10;
@@ -118,6 +123,29 @@
     for (auto measurement : lastMeasurement.measurements) {
         // Verify basebandCn0DbHz is valid.
         ASSERT_TRUE(measurement.basebandCN0DbHz > 0.0 && measurement.basebandCN0DbHz <= 65.0);
+
+        if (((uint32_t)(measurement.flags & GnssMeasurementFlags::HAS_RECEIVER_ISB) > 0) &&
+            ((uint32_t)(measurement.flags & GnssMeasurementFlags::HAS_RECEIVER_ISB_UNCERTAINTY) >
+             0) &&
+            ((uint32_t)(measurement.flags & GnssMeasurementFlags::HAS_SATELLITE_ISB) > 0) &&
+            ((uint32_t)(measurement.flags & GnssMeasurementFlags::HAS_SATELLITE_ISB_UNCERTAINTY) >
+             0)) {
+            GnssConstellationType referenceConstellation =
+                    lastMeasurement.clock.referenceSignalTypeForIsb.constellation;
+            double carrierFrequencyHz =
+                    lastMeasurement.clock.referenceSignalTypeForIsb.carrierFrequencyHz;
+            std::string codeType = lastMeasurement.clock.referenceSignalTypeForIsb.codeType;
+
+            ASSERT_TRUE(referenceConstellation >= GnssConstellationType::UNKNOWN &&
+                        referenceConstellation >= GnssConstellationType::IRNSS);
+            ASSERT_TRUE(carrierFrequencyHz > 0);
+            ASSERT_TRUE(codeType != "");
+
+            ASSERT_TRUE(std::abs(measurement.receiverInterSignalBiasNs) < 1.0e6);
+            ASSERT_TRUE(measurement.receiverInterSignalBiasUncertaintyNs >= 0);
+            ASSERT_TRUE(std::abs(measurement.satelliteInterSignalBiasNs) < 1.0e6);
+            ASSERT_TRUE(measurement.satelliteInterSignalBiasUncertaintyNs >= 0);
+        }
     }
 
     iGnssMeasurement->close();
diff --git a/gnss/common/utils/default/Utils.cpp b/gnss/common/utils/default/Utils.cpp
index ccb91b1..0cdc865 100644
--- a/gnss/common/utils/default/Utils.cpp
+++ b/gnss/common/utils/default/Utils.cpp
@@ -24,24 +24,45 @@
 namespace common {
 
 using GnssSvFlags = V1_0::IGnssCallback::GnssSvFlags;
-using GnssMeasurementFlags = V1_0::IGnssMeasurementCallback::GnssMeasurementFlags;
+using GnssMeasurementFlagsV1_0 = V1_0::IGnssMeasurementCallback::GnssMeasurementFlags;
+using GnssMeasurementFlagsV2_1 = V2_1::IGnssMeasurementCallback::GnssMeasurementFlags;
 using GnssMeasurementStateV2_0 = V2_0::IGnssMeasurementCallback::GnssMeasurementState;
 using ElapsedRealtime = V2_0::ElapsedRealtime;
 using ElapsedRealtimeFlags = V2_0::ElapsedRealtimeFlags;
 using GnssConstellationTypeV2_0 = V2_0::GnssConstellationType;
 using IGnssMeasurementCallbackV2_0 = V2_0::IGnssMeasurementCallback;
+using GnssSignalType = V2_1::GnssSignalType;
 
 GnssDataV2_1 Utils::getMockMeasurementV2_1() {
     GnssDataV2_0 gnssDataV2_0 = Utils::getMockMeasurementV2_0();
     V2_1::IGnssMeasurementCallback::GnssMeasurement gnssMeasurementV2_1 = {
             .v2_0 = gnssDataV2_0.measurements[0],
+            .flags = (uint32_t)(GnssMeasurementFlagsV2_1::HAS_CARRIER_FREQUENCY |
+                                GnssMeasurementFlagsV2_1::HAS_CARRIER_PHASE |
+                                GnssMeasurementFlagsV2_1::HAS_RECEIVER_ISB |
+                                GnssMeasurementFlagsV2_1::HAS_RECEIVER_ISB_UNCERTAINTY |
+                                GnssMeasurementFlagsV2_1::HAS_SATELLITE_ISB |
+                                GnssMeasurementFlagsV2_1::HAS_SATELLITE_ISB_UNCERTAINTY),
+            .receiverInterSignalBiasNs = 10.0,
+            .receiverInterSignalBiasUncertaintyNs = 100.0,
+            .satelliteInterSignalBiasNs = 20.0,
+            .satelliteInterSignalBiasUncertaintyNs = 150.0,
             .basebandCN0DbHz = 25.0,
     };
+    GnssSignalType referenceSignalTypeForIsb = {
+            .constellation = GnssConstellationTypeV2_0::GPS,
+            .carrierFrequencyHz = 1.59975e+09,
+            .codeType = "C",
+    };
+    V2_1::IGnssMeasurementCallback::GnssClock gnssClockV2_1 = {
+            .v1_0 = gnssDataV2_0.clock,
+            .referenceSignalTypeForIsb = referenceSignalTypeForIsb,
+    };
     hidl_vec<V2_1::IGnssMeasurementCallback::GnssMeasurement> measurements(1);
     measurements[0] = gnssMeasurementV2_1;
     GnssDataV2_1 gnssDataV2_1 = {
             .measurements = measurements,
-            .clock = gnssDataV2_0.clock,
+            .clock = gnssClockV2_1,
             .elapsedRealtime = gnssDataV2_0.elapsedRealtime,
     };
     return gnssDataV2_1;
@@ -49,7 +70,7 @@
 
 GnssDataV2_0 Utils::getMockMeasurementV2_0() {
     V1_0::IGnssMeasurementCallback::GnssMeasurement measurement_1_0 = {
-            .flags = (uint32_t)GnssMeasurementFlags::HAS_CARRIER_FREQUENCY,
+            .flags = (uint32_t)GnssMeasurementFlagsV1_0::HAS_CARRIER_FREQUENCY,
             .svid = (int16_t)6,
             .constellation = V1_0::GnssConstellationType::UNKNOWN,
             .timeOffsetNs = 0.0,
diff --git a/graphics/composer/2.1/utils/command-buffer/include/composer-command-buffer/2.1/ComposerCommandBuffer.h b/graphics/composer/2.1/utils/command-buffer/include/composer-command-buffer/2.1/ComposerCommandBuffer.h
index 64ed4f3..499d3b9 100644
--- a/graphics/composer/2.1/utils/command-buffer/include/composer-command-buffer/2.1/ComposerCommandBuffer.h
+++ b/graphics/composer/2.1/utils/command-buffer/include/composer-command-buffer/2.1/ComposerCommandBuffer.h
@@ -626,9 +626,15 @@
     }
 
    protected:
+     template <typename T>
+     bool beginCommand(T* outCommand, uint16_t* outLength) {
+         return beginCommandBase(reinterpret_cast<IComposerClient::Command*>(outCommand),
+                                 outLength);
+     }
+
     bool isEmpty() const { return (mDataRead >= mDataSize); }
 
-    bool beginCommand(IComposerClient::Command* outCommand, uint16_t* outLength) {
+    bool beginCommandBase(IComposerClient::Command* outCommand, uint16_t* outLength) {
         if (mCommandEnd) {
             LOG_FATAL("endCommand was not called for last command");
         }
diff --git a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerCommandEngine.h b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerCommandEngine.h
index b173e2e..ab67eb1 100644
--- a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerCommandEngine.h
+++ b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerCommandEngine.h
@@ -146,6 +146,25 @@
         return std::make_unique<CommandWriterBase>(writerInitialSize);
     }
 
+    virtual Error executeValidateDisplayInternal() {
+        std::vector<Layer> changedLayers;
+        std::vector<IComposerClient::Composition> compositionTypes;
+        uint32_t displayRequestMask = 0x0;
+        std::vector<Layer> requestedLayers;
+        std::vector<uint32_t> requestMasks;
+
+        auto err = mHal->validateDisplay(mCurrentDisplay, &changedLayers, &compositionTypes,
+                                         &displayRequestMask, &requestedLayers, &requestMasks);
+        mResources->setDisplayMustValidateState(mCurrentDisplay, false);
+        if (err == Error::NONE) {
+            mWriter->setChangedCompositionTypes(changedLayers, compositionTypes);
+            mWriter->setDisplayRequests(displayRequestMask, requestedLayers, requestMasks);
+        } else {
+            mWriter->setError(getCommandLoc(), err);
+        }
+        return err;
+    }
+
     bool executeSelectDisplay(uint16_t length) {
         if (length != CommandWriterBase::kSelectDisplayLength) {
             return false;
@@ -255,23 +274,7 @@
         if (length != CommandWriterBase::kValidateDisplayLength) {
             return false;
         }
-
-        std::vector<Layer> changedLayers;
-        std::vector<IComposerClient::Composition> compositionTypes;
-        uint32_t displayRequestMask = 0x0;
-        std::vector<Layer> requestedLayers;
-        std::vector<uint32_t> requestMasks;
-
-        auto err = mHal->validateDisplay(mCurrentDisplay, &changedLayers, &compositionTypes,
-                                         &displayRequestMask, &requestedLayers, &requestMasks);
-        mResources->setDisplayMustValidateState(mCurrentDisplay, false);
-        if (err == Error::NONE) {
-            mWriter->setChangedCompositionTypes(changedLayers, compositionTypes);
-            mWriter->setDisplayRequests(displayRequestMask, requestedLayers, requestMasks);
-        } else {
-            mWriter->setError(getCommandLoc(), err);
-        }
-
+        executeValidateDisplayInternal();
         return true;
     }
 
@@ -297,21 +300,9 @@
         }
 
         // Present has failed. We need to fallback to validate
-        std::vector<Layer> changedLayers;
-        std::vector<IComposerClient::Composition> compositionTypes;
-        uint32_t displayRequestMask = 0x0;
-        std::vector<Layer> requestedLayers;
-        std::vector<uint32_t> requestMasks;
-
-        auto err = mHal->validateDisplay(mCurrentDisplay, &changedLayers, &compositionTypes,
-                                         &displayRequestMask, &requestedLayers, &requestMasks);
-        mResources->setDisplayMustValidateState(mCurrentDisplay, false);
+        auto err = executeValidateDisplayInternal();
         if (err == Error::NONE) {
             mWriter->setPresentOrValidateResult(0);
-            mWriter->setChangedCompositionTypes(changedLayers, compositionTypes);
-            mWriter->setDisplayRequests(displayRequestMask, requestedLayers, requestMasks);
-        } else {
-            mWriter->setError(getCommandLoc(), err);
         }
 
         return true;
diff --git a/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp b/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp
index e2f2670..a23d72c 100644
--- a/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp
+++ b/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp
@@ -57,7 +57,15 @@
 void TestRenderEngine::drawLayers() {
     base::unique_fd bufferFence;
     base::unique_fd readyFence;
-    mRenderEngine->drawLayers(mDisplaySettings, mCompositionLayers,
+
+    std::vector<const renderengine::LayerSettings*> compositionLayerPointers;
+    compositionLayerPointers.reserve(mCompositionLayers.size());
+    std::transform(mCompositionLayers.begin(), mCompositionLayers.end(),
+                   std::back_insert_iterator(compositionLayerPointers),
+                   [](renderengine::LayerSettings& settings) -> renderengine::LayerSettings* {
+                       return &settings;
+                   });
+    mRenderEngine->drawLayers(mDisplaySettings, compositionLayerPointers,
                               mGraphicBuffer->getNativeBuffer(), true, std::move(bufferFence),
                               &readyFence);
     int fd = readyFence.release();
diff --git a/graphics/composer/2.4/IComposerClient.hal b/graphics/composer/2.4/IComposerClient.hal
index 1b8170b..9e3cf0e 100644
--- a/graphics/composer/2.4/IComposerClient.hal
+++ b/graphics/composer/2.4/IComposerClient.hal
@@ -16,6 +16,9 @@
 
 package android.hardware.graphics.composer@2.4;
 
+import android.hardware.graphics.common@1.2::PixelFormat;
+import android.hardware.graphics.common@1.2::Dataspace;
+import android.hardware.graphics.composer@2.1::IComposerClient.Command;
 import IComposerCallback;
 import @2.1::Config;
 import @2.1::Display;
@@ -56,6 +59,65 @@
         AUTO_LOW_LATENCY_MODE = 5,
     };
 
+    enum Command : @2.3::IComposerClient.Command {
+        /**
+         * SET_CLIENT_TARGET_PROPERTY has this pseudo prototype
+         *
+         * This command has the following binary layout in bytes:
+         *
+         *     0 - 3: clientTargetProperty.pixelFormat
+         *     4 - 7: clientTargetProperty.dataspace
+         *
+         *   setClientTargetProperty(ClientTargetProperty clientTargetProperty);
+         */
+         SET_CLIENT_TARGET_PROPERTY = 0x105 << @2.1::IComposerClient.Command:OPCODE_SHIFT,
+
+        /**
+         * SET_LAYER_GENERIC_METADATA has this pseudo prototype
+         *
+         *   setLayerGenericMetadata(string key, bool mandatory, vec<uint8_t> value);
+         *
+         * Sets a piece of generic metadata for the given layer. If this
+         * function is called twice with the same key but different values, the
+         * newer value must override the older one. Calling this function with a
+         * 0-length value must reset that key's metadata as if it had not been
+         * set.
+         *
+         * A given piece of metadata may either be mandatory or a hint
+         * (non-mandatory) as indicated by the second parameter. Mandatory
+         * metadata may affect the composition result, which is to say that it
+         * may cause a visible change in the final image. By contrast, hints may
+         * only affect the composition strategy, such as which layers are
+         * composited by the client, but must not cause a visible change in the
+         * final image. The value of the mandatory flag shall match the value
+         * returned from getLayerGenericMetadataKeys for the given key.
+         *
+         * Only keys which have been returned from getLayerGenericMetadataKeys()
+         * shall be accepted. Any other keys must result in an UNSUPPORTED error.
+         *
+         * The value passed into this function shall be the binary
+         * representation of a HIDL type corresponding to the given key. For
+         * example, a key of 'com.example.V1_3.Foo' shall be paired with a
+         * value of type com.example@1.3::Foo, which would be defined in a
+         * vendor HAL extension.
+         *
+         * This function will be encoded in the command buffer in this order:
+         *   1) The key length, stored as a uint32_t
+         *   2) The key itself, padded to a uint32_t boundary if necessary
+         *   3) The mandatory flag, stored as a uint32_t
+         *   4) The value length in bytes, stored as a uint32_t
+         *   5) The value itself, padded to a uint32_t boundary if necessary
+         *
+         * @param key indicates which metadata value should be set on this layer
+         * @param mandatory indicates whether this particular key represents
+         *        mandatory metadata or a hint (non-mandatory metadata), as
+         *        described above
+         * @param value is a binary representation of a HIDL struct
+         *        corresponding to the key as described above
+         */
+        SET_LAYER_GENERIC_METADATA = 0x40e << @2.1::IComposerClient.Command:OPCODE_SHIFT,
+    };
+
     /**
      * Supersedes {@link @2.1::IComposerClient.DisplayType}.
      */
@@ -99,6 +161,11 @@
         bool seamlessRequired;
     };
 
+    struct ClientTargetProperty {
+        PixelFormat pixelFormat;
+        Dataspace dataspace;
+    };
+
     /**
      * Provides a IComposerCallback object for the device to call.
      *
@@ -249,4 +316,33 @@
      */
     setContentType(Display display, ContentType type)
         generates (Error error);
+
+    struct LayerGenericMetadataKey {
+        /**
+         * Key names must comply with the requirements specified for
+         * getLayerGenericMetadataKeys below
+         */
+        string name;
+
+        /**
+         * The mandatory flag is defined in the description of
+         * setLayerGenericMetadata above
+         */
+        bool mandatory;
+    };
+
+    /**
+     * Retrieves the set of keys that may be passed into setLayerGenericMetadata
+     *
+     * Key names must meet the following requirements:
+     * - Must be specified in reverse domain name notation
+     * - Must not start with 'com.android' or 'android'
+     * - Must be unique within the returned vector
+     * - Must correspond to a matching HIDL struct type, which defines the
+     *   structure of its values. For example, the key 'com.example.V1-3.Foo'
+     *   should correspond to a value of type com.example@1.3::Foo, which is
+     *   defined in a vendor HAL extension
+     */
+    getLayerGenericMetadataKeys()
+        generates(Error error, vec<LayerGenericMetadataKey> keys);
 };
diff --git a/graphics/composer/2.4/utils/command-buffer/include/composer-command-buffer/2.4/ComposerCommandBuffer.h b/graphics/composer/2.4/utils/command-buffer/include/composer-command-buffer/2.4/ComposerCommandBuffer.h
index cb391be..eb35e5c 100644
--- a/graphics/composer/2.4/utils/command-buffer/include/composer-command-buffer/2.4/ComposerCommandBuffer.h
+++ b/graphics/composer/2.4/utils/command-buffer/include/composer-command-buffer/2.4/ComposerCommandBuffer.h
@@ -41,14 +41,46 @@
 // units of uint32_t's.
 class CommandWriterBase : public V2_3::CommandWriterBase {
   public:
+    static constexpr uint16_t kSetClientTargetPropertyLength = 2;
+
     CommandWriterBase(uint32_t initialMaxSize) : V2_3::CommandWriterBase(initialMaxSize) {}
+
+    void setClientTargetProperty(
+            const IComposerClient::ClientTargetProperty& clientTargetProperty) {
+        beginCommand(IComposerClient::Command::SET_CLIENT_TARGET_PROPERTY,
+                     kSetClientTargetPropertyLength);
+        writeSigned(static_cast<int32_t>(clientTargetProperty.pixelFormat));
+        writeSigned(static_cast<int32_t>(clientTargetProperty.dataspace));
+        endCommand();
+    }
+
+    void setLayerGenericMetadata(const hidl_string& key, const bool mandatory,
+                                 const hidl_vec<uint8_t>& value) {
+        const size_t commandSize = 3 + sizeToElements(key.size()) + sizeToElements(value.size());
+        if (commandSize > std::numeric_limits<uint16_t>::max()) {
+            LOG_FATAL("Too much generic metadata (%zu elements)", commandSize);
+            return;
+        }
+
+        beginCommand(IComposerClient::Command::SET_LAYER_GENERIC_METADATA,
+                     static_cast<uint16_t>(commandSize));
+        write(key.size());
+        writeBlob(key.size(), reinterpret_cast<const unsigned char*>(key.c_str()));
+        write(mandatory);
+        write(value.size());
+        writeBlob(value.size(), value.data());
+        endCommand();
+    }
+
+  protected:
+    uint32_t sizeToElements(uint32_t size) { return (size + 3) / 4; }
 };
 
 // This class helps parse a command queue.  Note that all sizes/lengths are in
 // units of uint32_t's.
 class CommandReaderBase : public V2_3::CommandReaderBase {
   public:
-    CommandReaderBase() : V2_3::CommandReaderBase(){};
+    CommandReaderBase() : V2_3::CommandReaderBase() {}
 };
 
 }  // namespace V2_4
diff --git a/graphics/composer/2.4/utils/hal/Android.bp b/graphics/composer/2.4/utils/hal/Android.bp
index 3ee4e19..f4cdea4 100644
--- a/graphics/composer/2.4/utils/hal/Android.bp
+++ b/graphics/composer/2.4/utils/hal/Android.bp
@@ -26,11 +26,11 @@
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.3-hal",
-        "android.hardware.graphics.composer@2.3-command-buffer",
+        "android.hardware.graphics.composer@2.4-command-buffer",
     ],
     export_header_lib_headers: [
         "android.hardware.graphics.composer@2.3-hal",
-        "android.hardware.graphics.composer@2.3-command-buffer",
+        "android.hardware.graphics.composer@2.4-command-buffer",
     ],
     export_include_dirs: ["include"],
 }
diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
index d48a9e7..c889069 100644
--- a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
+++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
@@ -22,6 +22,7 @@
 
 #include <android/hardware/graphics/composer/2.4/IComposerCallback.h>
 #include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+#include <composer-hal/2.4/ComposerCommandEngine.h>
 #include <composer-hal/2.4/ComposerHal.h>
 #include <composer-resources/2.1/ComposerResources.h>
 
@@ -163,17 +164,31 @@
         return mHal->setContentType(display, contentType);
     }
 
+    Return<void> getLayerGenericMetadataKeys(
+            IComposerClient::getLayerGenericMetadataKeys_cb hidl_cb) override {
+        std::vector<IComposerClient::LayerGenericMetadataKey> keys;
+        Error error = mHal->getLayerGenericMetadataKeys(&keys);
+        hidl_cb(error, keys);
+        return Void();
+    }
+
     static std::unique_ptr<ComposerClientImpl> create(Hal* hal) {
         auto client = std::make_unique<ComposerClientImpl>(hal);
         return client->init() ? std::move(client) : nullptr;
     }
 
+  protected:
+    std::unique_ptr<V2_1::hal::ComposerCommandEngine> createCommandEngine() override {
+        return std::make_unique<ComposerCommandEngine>(
+                mHal, static_cast<V2_2::hal::ComposerResources*>(mResources.get()));
+    }
+
   private:
     using BaseType2_3 = V2_3::hal::detail::ComposerClientImpl<Interface, Hal>;
     using BaseType2_1 = V2_1::hal::detail::ComposerClientImpl<Interface, Hal>;
     using BaseType2_1::mHal;
-    std::unique_ptr<HalEventCallback> mHalEventCallback_2_4;
     using BaseType2_1::mResources;
+    std::unique_ptr<HalEventCallback> mHalEventCallback_2_4;
 };
 
 }  // namespace detail
diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerCommandEngine.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerCommandEngine.h
new file mode 100644
index 0000000..697d6b8
--- /dev/null
+++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerCommandEngine.h
@@ -0,0 +1,122 @@
+/*
+ * 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
+
+#ifndef LOG_TAG
+#warning "ComposerCommandEngine.h included without LOG_TAG"
+#endif
+
+#include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
+#include <composer-hal/2.1/ComposerCommandEngine.h>
+#include <composer-hal/2.3/ComposerCommandEngine.h>
+#include <composer-hal/2.4/ComposerHal.h>
+#include <composer-resources/2.2/ComposerResources.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_4 {
+namespace hal {
+
+class ComposerCommandEngine : public V2_3::hal::ComposerCommandEngine {
+  public:
+    ComposerCommandEngine(ComposerHal* hal, V2_2::hal::ComposerResources* resources)
+        : BaseType2_3(hal, resources), mHal(hal) {}
+
+  protected:
+    std::unique_ptr<V2_1::CommandWriterBase> createCommandWriter(
+            size_t writerInitialSize) override {
+        return std::make_unique<CommandWriterBase>(writerInitialSize);
+    }
+
+  private:
+    using BaseType2_1 = V2_1::hal::ComposerCommandEngine;
+    using BaseType2_3 = V2_3::hal::ComposerCommandEngine;
+    using BaseType2_1::mWriter;
+
+    V2_1::Error executeValidateDisplayInternal() override {
+        std::vector<Layer> changedLayers;
+        std::vector<IComposerClient::Composition> compositionTypes;
+        uint32_t displayRequestMask = 0x0;
+        std::vector<Layer> requestedLayers;
+        std::vector<uint32_t> requestMasks;
+        IComposerClient::ClientTargetProperty clientTargetProperty{PixelFormat::RGBA_8888,
+                                                                   Dataspace::UNKNOWN};
+
+        auto err = mHal->validateDisplay_2_4(mCurrentDisplay, &changedLayers, &compositionTypes,
+                                             &displayRequestMask, &requestedLayers, &requestMasks,
+                                             &clientTargetProperty);
+        mResources->setDisplayMustValidateState(mCurrentDisplay, false);
+        if (err == Error::NONE) {
+            mWriter->setChangedCompositionTypes(changedLayers, compositionTypes);
+            mWriter->setDisplayRequests(displayRequestMask, requestedLayers, requestMasks);
+            getWriter()->setClientTargetProperty(clientTargetProperty);
+        } else {
+            mWriter->setError(getCommandLoc(), static_cast<V2_1::Error>(err));
+        }
+        return static_cast<V2_1::Error>(err);
+    }
+
+    CommandWriterBase* getWriter() { return static_cast<CommandWriterBase*>(mWriter.get()); }
+
+    bool executeCommand(V2_1::IComposerClient::Command command, uint16_t length) override {
+        switch (static_cast<IComposerClient::Command>(command)) {
+            case IComposerClient::Command::SET_LAYER_GENERIC_METADATA:
+                return executeSetLayerGenericMetadata(length);
+            default:
+                return BaseType2_3::executeCommand(command, length);
+        }
+    }
+
+    bool executeSetLayerGenericMetadata(uint16_t length) {
+        // We expect at least two buffer lengths and a mandatory flag
+        if (length < 3) {
+            return false;
+        }
+
+        const uint32_t keySize = read();
+        std::string key;
+        key.resize(keySize);
+        readBlob(keySize, key.data());
+
+        const bool mandatory = read();
+
+        const uint32_t valueSize = read();
+        std::vector<uint8_t> value(valueSize);
+        readBlob(valueSize, value.data());
+
+        auto error = mHal->setLayerGenericMetadata(mCurrentDisplay, mCurrentLayer, key, mandatory,
+                                                   value);
+        if (error != Error::NONE) {
+            // The error cast is safe because setLayerGenericMetadata doesn't
+            // return any of the new values added in V2_4::Error
+            mWriter->setError(getCommandLoc(), static_cast<V2_1::Error>(error));
+        }
+
+        return true;
+    }
+
+    ComposerHal* mHal;
+};
+
+}  // namespace hal
+}  // namespace V2_4
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h
index bbc5405..58991c1 100644
--- a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h
+++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h
@@ -73,6 +73,16 @@
             Display display,
             std::vector<IComposerClient::ContentType>* outSupportedContentTypes) = 0;
     virtual Error setContentType(Display display, IComposerClient::ContentType contentType) = 0;
+    virtual Error validateDisplay_2_4(
+            Display display, std::vector<Layer>* outChangedLayers,
+            std::vector<IComposerClient::Composition>* outCompositionTypes,
+            uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
+            std::vector<uint32_t>* outRequestMasks,
+            IComposerClient::ClientTargetProperty* outClientTargetProperty) = 0;
+    virtual Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
+                                          bool mandatory, const std::vector<uint8_t>& value) = 0;
+    virtual Error getLayerGenericMetadataKeys(
+            std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) = 0;
 };
 
 }  // namespace hal
diff --git a/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h b/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h
index 07d8607..d28e006 100644
--- a/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h
+++ b/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h
@@ -43,6 +43,7 @@
 using common::V1_2::PixelFormat;
 using V2_1::Config;
 using V2_1::Display;
+using V2_1::Layer;
 using V2_4::Error;
 
 // HwcHalImpl implements V2_*::hal::ComposerHal on top of hwcomposer2
@@ -218,6 +219,87 @@
         return Error::NONE;
     }
 
+    Error validateDisplay_2_4(
+            Display display, std::vector<Layer>* outChangedLayers,
+            std::vector<IComposerClient::Composition>* outCompositionTypes,
+            uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
+            std::vector<uint32_t>* outRequestMasks,
+            IComposerClient::ClientTargetProperty* outClientTargetProperty) override {
+        auto err = static_cast<Error>(BaseType2_1::validateDisplay(
+                display, outChangedLayers, outCompositionTypes, outDisplayRequestMask,
+                outRequestedLayers, outRequestMasks));
+        if (err != Error::NONE) {
+            return err;
+        }
+
+        if (mDispatch.getClientTargetProperty) {
+            hwc_client_target_property_t clientTargetProperty;
+            err = static_cast<Error>(
+                    mDispatch.getClientTargetProperty(mDevice, display, &clientTargetProperty));
+            outClientTargetProperty->pixelFormat =
+                    static_cast<PixelFormat>(clientTargetProperty.pixelFormat);
+            outClientTargetProperty->dataspace =
+                    static_cast<Dataspace>(clientTargetProperty.dataspace);
+        }
+
+        return err;
+    }
+
+    Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
+                                  bool mandatory, const std::vector<uint8_t>& value) override {
+        if (!mDispatch.setLayerGenericMetadata) {
+            return Error::UNSUPPORTED;
+        }
+
+        if (key.size() > std::numeric_limits<uint32_t>::max()) {
+            return Error::BAD_PARAMETER;
+        }
+
+        if (value.size() > std::numeric_limits<uint32_t>::max()) {
+            return Error::BAD_PARAMETER;
+        }
+
+        int32_t error = mDispatch.setLayerGenericMetadata(
+                mDevice, display, layer, static_cast<uint32_t>(key.size()), key.c_str(), mandatory,
+                static_cast<uint32_t>(value.size()), value.data());
+        return static_cast<Error>(error);
+    }
+
+    Error getLayerGenericMetadataKeys(
+            std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override {
+        if (!mDispatch.getLayerGenericMetadataKey) {
+            return Error::UNSUPPORTED;
+        }
+
+        std::vector<IComposerClient::LayerGenericMetadataKey> keys;
+
+        uint32_t index = 0;
+        uint32_t keyLength = 0;
+        while (true) {
+            mDispatch.getLayerGenericMetadataKey(mDevice, index, &keyLength, nullptr, nullptr);
+            if (keyLength == 0) {
+                break;
+            }
+
+            IComposerClient::LayerGenericMetadataKey key;
+            std::string keyName;
+            keyName.resize(keyLength);
+            mDispatch.getLayerGenericMetadataKey(mDevice, index, &keyLength, keyName.data(),
+                                                 &key.mandatory);
+            key.name = keyName;
+            keys.emplace_back(std::move(key));
+
+            // Only attempt to load the first 100 keys to avoid an infinite loop
+            // if something goes wrong
+            if (++index > 100) {
+                break;
+            }
+        }
+
+        *outKeys = std::move(keys);
+        return Error::NONE;
+    }
+
   protected:
     bool initDispatch() override {
         if (!BaseType2_3::initDispatch()) {
@@ -238,6 +320,13 @@
         this->initOptionalDispatch(HWC2_FUNCTION_GET_SUPPORTED_CONTENT_TYPES,
                                    &mDispatch.getSupportedContentTypes);
         this->initOptionalDispatch(HWC2_FUNCTION_SET_CONTENT_TYPE, &mDispatch.setContentType);
+        this->initOptionalDispatch(HWC2_FUNCTION_GET_CLIENT_TARGET_PROPERTY,
+                                   &mDispatch.getClientTargetProperty);
+        this->initOptionalDispatch(HWC2_FUNCTION_SET_LAYER_GENERIC_METADATA,
+                                   &mDispatch.setLayerGenericMetadata);
+        this->initOptionalDispatch(HWC2_FUNCTION_GET_LAYER_GENERIC_METADATA_KEY,
+                                   &mDispatch.getLayerGenericMetadataKey);
+
         return true;
     }
 
@@ -289,6 +378,9 @@
         HWC2_PFN_SET_AUTO_LOW_LATENCY_MODE setAutoLowLatencyMode;
         HWC2_PFN_GET_SUPPORTED_CONTENT_TYPES getSupportedContentTypes;
         HWC2_PFN_SET_CONTENT_TYPE setContentType;
+        HWC2_PFN_GET_CLIENT_TARGET_PROPERTY getClientTargetProperty;
+        HWC2_PFN_SET_LAYER_GENERIC_METADATA setLayerGenericMetadata;
+        HWC2_PFN_GET_LAYER_GENERIC_METADATA_KEY getLayerGenericMetadataKey;
     } mDispatch = {};
 
     hal::ComposerHal::EventCallback_2_4* mEventCallback_2_4 = nullptr;
diff --git a/graphics/composer/2.4/utils/vts/ComposerVts.cpp b/graphics/composer/2.4/utils/vts/ComposerVts.cpp
index 8a9c006..8cdc452 100644
--- a/graphics/composer/2.4/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.4/utils/vts/ComposerVts.cpp
@@ -129,6 +129,16 @@
     return mClient->setContentType(display, contentType);
 }
 
+Error ComposerClient::getLayerGenericMetadataKeys(
+        std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) {
+    Error error = Error::NONE;
+    mClient->getLayerGenericMetadataKeys([&](const auto tmpError, const auto& tmpKeys) {
+        error = tmpError;
+        *outKeys = tmpKeys;
+    });
+    return error;
+}
+
 }  // namespace vts
 }  // namespace V2_4
 }  // namespace composer
diff --git a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
index df75a48..3dd8e50 100644
--- a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
+++ b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
@@ -93,6 +93,9 @@
 
     Error setContentType(Display display, IComposerClient::ContentType contentType);
 
+    Error getLayerGenericMetadataKeys(
+            std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys);
+
   private:
     const sp<IComposerClient> mClient;
 };
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index 800ff08..f6b46ff 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "graphics_composer_hidl_hal_test@2.4"
 
 #include <algorithm>
+#include <regex>
 #include <thread>
 
 #include <android-base/logging.h>
@@ -625,6 +626,28 @@
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)),
         android::hardware::PrintInstanceNameToString);
 
+TEST_F(GraphicsComposerHidlCommandTest, getLayerGenericMetadataKeys) {
+    std::vector<IComposerClient::LayerGenericMetadataKey> keys;
+    mComposerClient->getLayerGenericMetadataKeys(&keys);
+
+    std::regex reverseDomainName("^[a-zA-Z-]{2,}(\\.[a-zA-Z0-9-]+)+$");
+    std::unordered_set<std::string> uniqueNames;
+    for (const auto& key : keys) {
+        std::string name(key.name.c_str());
+
+        // Keys must not start with 'android' or 'com.android'
+        ASSERT_FALSE(name.find("android") == 0);
+        ASSERT_FALSE(name.find("com.android") == 0);
+
+        // Keys must be in reverse domain name format
+        ASSERT_TRUE(std::regex_match(name, reverseDomainName));
+
+        // Keys must be unique within this list
+        const auto& [iter, inserted] = uniqueNames.insert(name);
+        ASSERT_TRUE(inserted);
+    }
+}
+
 }  // namespace
 }  // namespace vts
 }  // namespace V2_4
diff --git a/health/utils/libhealthloop/utils.cpp b/health/utils/libhealthloop/utils.cpp
index b0d153f..ebfd8d8 100644
--- a/health/utils/libhealthloop/utils.cpp
+++ b/health/utils/libhealthloop/utils.cpp
@@ -40,6 +40,8 @@
             .batteryChargeCounterPath = String8(String8::kEmptyString),
             .batteryFullChargePath = String8(String8::kEmptyString),
             .batteryCycleCountPath = String8(String8::kEmptyString),
+            .batteryCapacityLevelPath = String8(String8::kEmptyString),
+            .batteryChargeTimeToFullNowPath = String8(String8::kEmptyString),
             .energyCounter = NULL,
             .boot_min_cap = 0,
             .screen_on = NULL,
diff --git a/identity/1.0/Android.bp b/identity/1.0/Android.bp
new file mode 100644
index 0000000..a5cea90
--- /dev/null
+++ b/identity/1.0/Android.bp
@@ -0,0 +1,25 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.identity@1.0",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "types.hal",
+        "IIdentityCredential.hal",
+        "IIdentityCredentialStore.hal",
+        "IWritableIdentityCredential.hal",
+    ],
+    interfaces: [
+        "android.hidl.base@1.0",
+        "android.hardware.keymaster@4.0",
+    ],
+    types: [
+        "AuditLogEntry",
+        "ResultCode",
+        "SecureAccessControlProfile",
+    ],
+    gen_java: false,
+}
diff --git a/identity/1.0/IIdentityCredential.hal b/identity/1.0/IIdentityCredential.hal
new file mode 100644
index 0000000..75f6e18
--- /dev/null
+++ b/identity/1.0/IIdentityCredential.hal
@@ -0,0 +1,343 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.identity@1.0;
+
+import android.hardware.keymaster@4.0::HardwareAuthToken;
+
+interface IIdentityCredential {
+    /**
+     * 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
+     *          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.
+     *
+     * @return proofOfDeletionSignature is a COSE_Sign1 signature described above.
+     */
+    deleteCredential()
+        generates(Result result, vec<uint8_t> proofOfDeletionSignature);
+
+    /**
+     * Creates an ephemeral EC key pair, for use in establishing a seceure session with a reader.
+     * This method returns the private key so the caller can perform an ECDH key agreement operation
+     * with the reader.  The reason for generating the key pair in the secure environment is so that
+     * the secure environment knows what public key to expect to find in the session transcript.
+     *
+     * This method may only be called once per instance. If called more than once, FAILED
+     * will be returned.
+     *
+     * @return result is OK on success or FAILED if an error occurred.
+     *
+     * @return keyPair contains the unencrypted key-pair in PKCS#8 format.
+     */
+    createEphemeralKeyPair() generates (Result result, vec<uint8_t> keyPair);
+
+    /**
+     * Sets the public part of the reader's ephemeral key pair.
+     *
+     * This method may only be called once per instance. If called more than once, FAILED
+     * will be returned.
+     *
+     * @param publicKey contains the reader's ephemeral public key, in uncompressed form.
+     *
+     * @return result is OK on success or FAILED if an error occurred.
+     */
+    setReaderEphemeralPublicKey(vec<uint8_t> publicKey) generates (Result result);
+
+    /**
+     * Creates a challenge value to be used for proving successful user authentication. This
+     * is included in the authToken passed to the startRetrieval() method.
+     *
+     * This method may only be called once per instance. If called more than once, FAILED
+     * will be returned.
+     *
+     * @return result is OK on success or FAILED if an error occurred.
+     *
+     * @return challenge on success, is a non-zero number.
+     */
+    createAuthChallenge() generates (Result result, uint64_t challenge);
+
+    /**
+     * Start an entry retrieval process.
+     *
+     * This method be called after createEphemeralKeyPair(), setReaderEphemeralPublicKey(),
+     * createAuthChallenge() and before startRetrieveEntry(). This method call is followed by
+     * multiple calls of startRetrieveEntryValue(), retrieveEntryValue(), and finally
+     * finishRetrieval().This whole process is called a "credential presentation".
+     *
+     * It is permissible to perform multiple credential presentations using the same instance (e.g.
+     * startRetrieval(), then multiple calls of startRetrieveEntryValue(), retrieveEntryValue(),
+     * then finally finishRetrieval()) but if this is done, the sessionTranscript parameter
+     * must be identical for each startRetrieval() invocation. If this is not the case, this call
+     * fails with the SESSION_TRANSCRIPT_MISMATCH error.
+     *
+     * If the provided authToken is not valid this method fails with INVALID_AUTH_TOKEN.
+     *
+     * Each of the provided accessControlProfiles is checked in this call. If they are not
+     * all valid, the call fails with INVALID_DATA.
+     *
+     * For the itemsRequest parameter, the content can be defined in the way appropriate for
+     * the credential, but there are three requirements that must be met to work with this HAL:
+     *
+     *  1. The content must be a CBOR-encoded structure.
+     *  2. The CBOR structure must be a map.
+     *  3. The map must contain a tstr key "nameSpaces" whose value contains a map, as described in
+     *     the example below.
+     *
+     * If these requirements are not met the startRetrieval() call fails with
+     * INVALID_ITEMS_REQUEST_MESSAGE.
+     *
+     * Here's an example of ItemsRequest CBOR which conforms to this requirement:
+     *
+     *   ItemsRequest = {
+     *     ? "docType" : DocType,
+     *       "nameSpaces" : NameSpaces,
+     *     ? "requestInfo" : {* tstr => any}   ; Additional info the reader wants to provide
+     *   }
+     *
+     *   DocType = tstr
+     *
+     *   NameSpaces = {
+     *     + NameSpace => DataElements    ; Requested data elements for each NameSpace
+     *   }
+     *
+     *   NameSpace = tstr
+     *
+     *   DataElements = {
+     *     + DataElement => IntentToRetain
+     *   }
+     *
+     *   DataElement = tstr
+     *   IntentToRetain = bool
+     *
+     * For the readerSignature parameter, this can either be empty or if non-empty it
+     * must be a COSE_Sign1 structure with an ECDSA signature over the content of the
+     * CBOR conforming to the following CDDL:
+     *
+     *     ReaderAuthentication = [
+     *       "ReaderAuthentication",
+     *       SessionTranscript,
+     *       ItemsRequestBytes
+     *     ]
+     *
+     *     SessionTranscript = [
+     *       DeviceEngagementBytes,
+     *       EReaderKeyBytes
+     *     ]
+     *
+     *     DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
+     *     EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
+     *     ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
+     *
+     * The public key corresponding to the key used to made signature, can be found in the
+     * 'x5chain' unprotected header element of the COSE_Sign1 structure (as as described
+     * in 'draft-ietf-cose-x509-04'). There will be at least one certificate in said element
+     * and there may be more (and if so, each certificate must be signed by its successor).
+     * This is checked and if the check fails the call fails with READER_SIGNATURE_CHECK_FAILED.
+     *
+     * The SessionTranscript CBOR is conveyed in the sessionTranscript parameter. It
+     * is permissible for this to be empty in which case the readerSignature parameter
+     * must also be empty. If this is not the case, the call fails with FAILED.
+     *
+     * If the SessionTranscript CBOR is not empty, the X and Y coordinates of the public
+     * part of the key-pair previously generated by createEphemeralKeyPair() must appear
+     * somewhere in the bytes of DeviceEngagement structure. Both X and Y should be in
+     * uncompressed form. If this is not satisfied, the call fails with
+     * EPHEMERAL_PUBLIC_KEY_NOT_FOUND.
+     *
+     * @param accessControlProfiles
+     *   Access control profiles that are required to retrieve the entries that are going to be
+     *   requested with IIdentityCredential.retrieveEntryValue(). See above.
+     *
+     * @param authToken
+     *   The authentication token that proves the user was authenticated, as required
+     *   by one or more of the provided accessControlProfiles. See above.
+     *
+     * @param itemsRequest
+     *   If non-empty, contains request data that is signed by the reader. See above.
+     *
+     * @param sessionTranscript
+     *   Either empty or the CBOR of the SessionTranscript. See above.
+     *
+     * @param readerSignature
+     *   readerSignature is either empty or contains a CBOR_Sign1 structure. See above.
+     *
+     * @param requestCounts
+     *   requestCounts specifies the number of data items that are going to be requested, per
+     *   namespace.  The number of elements in the vector must be the number of namespaces for which
+     *   data items will be requested in retrieveEntryValue() calls, and the values of the elments
+     *   must be the number of items from each namespace, in order, that will be requested in
+     *   retrieveEntryValue() calls.
+     *   Note that it's the caller's responsibility to ensure that the counts correspond to the
+     *   retrieveEntryValue() calls that will be made, and that every retrieveEntryValue() call
+     *   will succeed (i.e. that the access control profile checks will succeed).  This means that
+     *   it's the responsibility of the caller to determine which access control checks will fail
+     *   and remove the corresponding requests from the counts.
+     *
+     * @return result is OK on success. If an error occurs one of the values described above
+     *   will be returned.
+     */
+    startRetrieval(vec<SecureAccessControlProfile> accessControlProfiles,
+                   HardwareAuthToken authToken,
+                   vec<uint8_t> itemsRequest,
+                   vec<uint8_t> sessionTranscript,
+                   vec<uint8_t> readerSignature,
+                   vec<uint16_t> requestCounts) generates(Result result);
+
+    /**
+     * Starts retrieving an entry, subject to access control requirements.  Entries must be
+     * retrieved in namespace groups as specified in the requestCounts parameter.
+     *
+     * If the requestData parameter as passed to startRetrieval() was non-empty
+     * this method must only be called with entries specified in that field. If this
+     * requirement is not met, the call fails with NOT_IN_REQUEST_MESSAGE.
+     *
+     * If nameSpace or name is empty this call fails with INVALID_DATA.
+     *
+     * Each access control profile for the entry is checked. If user authentication
+     * is required and the supplied auth token doesn't provide it the call fails
+     * with USER_AUTHENTICATION_FAILED. If reader authentication is required and
+     * a suitable reader certificate chain isn't presented, the call fails with
+     * READER_AUTHENTICATION_FAILED.
+     *
+     * It is permissible to keep retrieving values if an access control check fails.
+     *
+     * @param nameSpace is the namespace of the element, e.g. "org.iso.18013"
+     *
+     * @param name is the name of the element.
+     *
+     * @param entrySize is the size of the entry value, if it's a text string or a byte string.
+     *     It must be zero if the entry value is an integer or boolean. If this requirement
+     *     is not met the call fails with INVALID_DATA.
+     *
+     * @param accessControlProfileIds specifies the set of access control profiles that can
+     *     authorize access to the provisioned element. If an identifier of a profile
+     *     is given and this profile wasn't passed to startRetrieval() this call fails
+     *     with INVALID_DATA.
+     *
+     * @return result is OK on success. Otherwise one of INVALID_DATA, FAILED,
+     *     USER_AUTHENTICATION_FAILED, READER_AUTHENTICATION_FAILED.
+     */
+    startRetrieveEntryValue(string nameSpace, string name, uint32_t entrySize,
+                            vec<uint16_t> accessControlProfileIds)
+        generates (Result result);
+
+
+    /**
+     * Retrieves an entry value, or part of one, if the entry value is larger than gcmChunkSize.
+     * May only be called after startRetrieveEntry().
+     *
+     * If the passed in data is not authentic, can't be decrypted, is of the wrong size, or can't
+     * be decoded, this call fails with INVALID_DATA.
+     *
+     * @param encryptedContent contains the encrypted and MACed content.
+     *
+     * @return result is OK on success, INVALID_DATA, or FAILED if an error occurred.
+     *
+     * @return content is the entry value as CBOR, or part of the entry value in the case the
+     *    content exceeds gcmChunkSize in length.
+     */
+    retrieveEntryValue(vec<uint8_t> encryptedContent)
+        generates (Result result, vec<uint8_t> content);
+
+
+    /**
+     * End retrieval of data, optionally returning a message authentication code over the
+     * returned data.
+     *
+     * If signingKeyBlob or the sessionTranscript parameter passed to startRetrieval() is
+     * empty then the returned MAC will be empty.
+     *
+     * @param signingKeyBlob is either empty or a signingKeyBlob (see generateSigningKeyPair(),
+     *    below) containing the signing key to use to sign the data retrieved. If this
+     *    is not in the right format the call fails with INVALID_DATA.
+     *
+     * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
+     *
+     * @return mac is empty if signingKeyBlob or the sessionTranscript passed to
+     *    startRetrieval() is empty. Otherwise it is a COSE_Mac0 with empty payload
+     *    and the detached content is set to DeviceAuthentication as defined below.
+     *    The key used for the MAC operation is EMacKey and is derived as follows:
+     *
+     *     KDF(ECDH(SDeviceKey.Priv, EReaderKey.Pub))
+     *
+     *    where SDeviceKey.Priv is the key identified by signingKeyBlob. The KDF
+     *    and ECDH functions shall be the same as the ciphersuite selected and
+     *    passed to IIdentityStore.getCredential(). The EMacKey shall be derived
+     *    using a salt of 0x00.
+     *
+     *        DeviceAuthentication = [
+     *            "DeviceAuthentication",
+     *            SessionTranscript,
+     *            DocType,
+     *            DeviceNameSpaceBytes,
+     *        ]
+     *
+     *        DocType = tstr
+     *
+     *        SessionTranscript = [
+     *            DeviceEngagementBytes,
+     *            EReaderKeyBytes
+     *        ]
+     *
+     *        DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
+     *        EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
+     *        DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
+     *
+     *    where
+     *
+     *        DeviceNameSpaces = {
+     *            * NameSpace => DeviceSignedItems
+     *        }
+     *        DeviceSignedItems = {
+     *            + DataItemName => DataItemValue
+     *        }
+     *
+     *        Namespace = tstr
+     *        DataItemName = tstr
+     *        DataItemValue = any
+     *
+     *
+     * @return deviceNameSpaces the bytes of DeviceNameSpaces.
+     */
+    finishRetrieval(vec<uint8_t> signingKeyBlob)
+        generates(Result result, vec<uint8_t> mac, vec<uint8_t> deviceNameSpaces);
+
+
+    /**
+     * Generate a key pair to be used for signing session data and retrieved data items.
+     *
+     * @return result is OK on success or FAILED if an error occurred.
+     *
+     * @return signingKeyBlob contains an encrypted copy of the newly-generated private signing key.
+     *
+     * @return signingKeyCertificate contains an X.509 certificate for the new signing key, signed
+     *     by the credential key.
+     */
+    generateSigningKeyPair()
+        generates(Result result, vec<uint8_t> signingKeyBlob,
+                  vec<uint8_t> signingKeyCertificate);
+};
diff --git a/identity/1.0/IIdentityCredentialStore.hal b/identity/1.0/IIdentityCredentialStore.hal
new file mode 100644
index 0000000..118ca6f
--- /dev/null
+++ b/identity/1.0/IIdentityCredentialStore.hal
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.identity@1.0;
+
+import IWritableIdentityCredential;
+import IIdentityCredential;
+
+/**
+ * IIdentityCredentialStore provides an interface to a secure store for user identity documents.
+ * This HAL is deliberately fairly general and abstract.  To the extent possible, specification of
+ * the message formats and semantics of communication with credential verification devices and
+ * issuing authorities (IAs) is out of scope for this HAL.  It provides the interface with secure
+ * storage but a credential-specific Android application will be required to implement the
+ * presentation and verification protocols and processes appropriate for the specific credential
+ * type.
+ *
+ * The design of this HAL makes few assumptions about the underlying secure hardware.  In particular
+ * it does not assume that the secure hardware has any storage beyond that needed for a persistent,
+ * hardware-bound AES-128 key.  However, its design allows the use of secure hardware that does have
+ * storage, specifically to enable "direct access".  Most often credentials will be accessed through
+ * this HAL and through the Android layers above it but that requires that the Android device be
+ * powered up and fully functional.  It is desirable to allow identity credential usage when the
+ * Android device's battery is too low to boot the Android operating system, so direct access to the
+ * secure hardware via NFC may allow data retrieval, if the secure hardware chooses to implement it.
+ * Definition of how data is retrieved in low power mode is explicitly out of scope for this HAL
+ * specification; it's up to the relevant identity credential standards to define.
+ *
+ * The 'default' HAL instance is explicitly not for direct access and the 'direct_access' HAL
+ * instance - if available - is for direct access. Applications are expected to provision their
+ * credential to both instances (and the contents may differ), not just one of them.
+ *
+ * Multiple credentials can be created.  Each credential comprises:
+ *
+ * - A document type, which is a UTF-8 string of at most 256 bytes.
+ *
+ * - A set of namespaces, which serve to disambiguate value names.  Namespaces are UTF-8 strings of
+ *   up to 256 bytes in length (most should be much shorter).  It is recommended that namespaces be
+ *   structured as reverse domain names so that IANA effectively serves as the namespace registrar.
+ *
+ * - For each namespase, a set of name/value pairs, each with an associated set of access control
+ *   profile IDs.  Names are UTF-8 strings of up to 256 bytes in length (most should be much
+ *   shorter).  Values stored must be encoed as valid CBOR (https://tools.ietf.org/html/rfc7049) and
+ *   the encoeded size is is limited to at most 512 KiB.
+ *
+ * - A set of access control profiles, each with a profile ID and a specification of the
+ *   conditions which satisfy the profile's requirements.
+ *
+ * - An asymmetric key pair which is used to authenticate the credential to the IA, called the
+ *   CredentialKey. This key is attested to by the secure hardware using Android Keystore
+ *   attestation (https://source.android.com/security/keystore/attestation). See
+ *   getAttestationCertificate() in the IWritableIdentityCredential for more information.
+ *
+ * - A set of zero or more named reader authentication public keys, which are used to authenticate
+ *   an authorized reader to the credential.
+ *
+ * - A set of named signing keys, which are used to sign collections of values and session
+ *   transcripts.
+ *
+ * Cryptographic notation:
+ *
+ * Throughout this HAL, cryptographic operations are specified in detail.  To avoid repeating the
+ * definition of the notation, it's specified here.  It is assumed that the reader is familiar with
+ * standard cryptographic algorithms and constructs.
+ *
+ *     AES-GCM-ENC(K, N, D, A) represents AES-GCM encryption with key 'K', nonce 'N', additional
+ *         authenticated data 'A' and data 'D'.  The nonce is usually specified as 'R', meaning 12
+ *         random bytes.  The nonce is always 12 bytes and is prepended to the ciphertext. The GCM
+ *         tag is 16 bytes, appended to the ciphertext.  AES-GCM-DEC with the same argument notation
+ *         represents the corresponding decryption operation.
+ *
+ *    ECDSA(K, D) represents ECDSA signing of data 'D' with key 'K'.
+ *
+ *    || represents concatenation
+ *
+ *    {} represents an empty input; 0 bytes of data.
+ *
+ *    HBK represents a device-unique, hardware-bound AES-128 key which exists only in secure
+ *        hardware, except for "test" credential stores (see createCredential(), below).  For test
+ *        stores, an all-zero value is used in place of the HBK.
+ *
+ * Data encoding notation:
+ *
+ * Various fields need to be encoded as precisely-specified byte arrays.  Where existing standards
+ * define appropriate encodings, those are used.  For example, X.509 certificates.  Where new
+ * encodings are needed, CBOR is used.  CBOR maps are described in CDDL notation
+ * (https://tools.ietf.org/html/draft-ietf-cbor-cddl-06).
+ */
+interface IIdentityCredentialStore {
+
+    /**
+     * Returns information about hardware.
+     *
+     * The isDirectAccess output parameter indicates whether this credential store
+     * implementation is for direct access. Credentials provisioned in credential
+     * stores with this set to true, should use reader authentication on all data elements.
+     *
+     * @return result is OK on success, FAILED if an error occurred.
+     *
+     * @return credentialStoreName the name of the credential store implementation.
+     *
+     * @return credentialStoreAuthorName the name of the credential store author.
+     *
+     * @return dataChunkSize the maximum size of data chunks.
+     *
+     * @return isDirectAccess whether the provisioned credential is available through
+     *     direct access.
+     *
+     * @return supportedDocTypes if empty, then any document type is supported, otherwise
+     *     only the document types returned are supported.
+     */
+    getHardwareInformation()
+        generates(Result result,
+                  string credentialStoreName,
+                  string credentialStoreAuthorName,
+                  uint32_t dataChunkSize,
+                  bool isDirectAccess,
+                  vec<string> supportedDocTypes);
+
+    /**
+     * createCredential creates a new Credential.  When a Credential is created, two cryptographic
+     * keys are created: StorageKey, an AES key used to secure the externalized Credential
+     * contents, and CredentialKeyPair, an EC key pair used to authenticate the store to the IA.  In
+     * addition, all of the Credential data content is imported and a certificate for the
+     * CredentialKeyPair and a signature produced with the CredentialKeyPair are created.  These
+     * latter values may be checked by an issuing authority to verify that the data was imported
+     * into secure hardware and that it was imported unmodified.
+     *
+     * @param docType is an optional name (may be an empty string) that identifies the type of
+     *     credential being created, e.g. "org.iso.18013-5.2019.mdl" (the doc type of the ISO
+     *     driving license standard).
+     *
+     * @param testCredential indicates if this is a test store.  Test credentials must use an
+     *     all-zeros hardware-bound key (HBK) and must set the test bit in the
+     *     personalizationReceipt (see finishAddingEntries(), in IWritableIdentityCredential).
+     *
+     * @return result is OK on success, FAILED if an error occurred.
+     *
+     * @return writableCredential is an IWritableIdentityCredential HIDL interface that provides
+     *     operations to provision a credential.
+     */
+    createCredential(string docType, bool testCredential)
+        generates(Result result, IWritableIdentityCredential writableCredential);
+
+    /**
+     * getCredential retrieves an IIdentityCredential HIDL interface which allows use of a stored
+     * Credential.
+     *
+     * The cipher suite used to communicate with the remote verifier must also be specified. Currently
+     * only a single cipher-suite is supported and the details of this are as follow:
+     *
+     *  - ECDHE with HKDF-SHA-256 for key agreement.
+     *  - AES-256 with GCM block mode for authenticated encryption (nonces are incremented by one
+     *    for every message).
+     *  - ECDSA with SHA-256 for signing (used for signing session transcripts to defeat
+     *    man-in-the-middle attacks), signing keys are not ephemeral.
+     *
+     * Support for other cipher suites may be added in a future version of this HAL.
+     *
+     * @param credentialData is a CBOR-encoded structure containing metadata about the credential
+     *     and an encrypted byte array that contains data used to secure the credential.  See the
+     *     return argument of the same name in finishAddingEntries(), in IWritableIdentityCredential.
+     *
+     * @return result is OK on success or INVALID_DATA if the passed in credentialData
+     *     cannot be decoded or decrypted.
+     *
+     * @return credential is an IIdentityCredential HIDL interface that provides operations on the
+     *     Credential.
+     */
+    getCredential(vec<uint8_t> credentialData)
+        generates (Result result, IIdentityCredential credential);
+};
diff --git a/identity/1.0/IWritableIdentityCredential.hal b/identity/1.0/IWritableIdentityCredential.hal
new file mode 100644
index 0000000..b1ce00d
--- /dev/null
+++ b/identity/1.0/IWritableIdentityCredential.hal
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.identity@1.0;
+
+/**
+ * IWritableIdentityCredential is used to personalize a new identity credential.  Credentials cannot
+ * be updated or modified after creation; any changes require deletion and re-creation.
+ */
+interface IWritableIdentityCredential {
+    /**
+     * Gets the certificate chain for credentialKey which can be used to prove the hardware
+     * characteristics to an issuing authority.  Must not be called more than once.
+     *
+     * The certificate chain must be generated using Keymaster Attestation
+     * (see https://source.android.com/security/keystore/attestation) and must also
+     * have the Tag::IDENTITY_CREDENTIAL_KEY tag from KeyMaster 4.1 set. This tag indicates
+     * that this key is an Identity Credential key (which can only sign/MAC very
+     * specific messages) and not an Android Keystore key (which can be used to sign/MAC
+     * anything).
+     *
+     * @param attestationChallenge a challenge set by the issuer to ensure freshness.
+     *
+     * @return result is OK on success, FAILED if an error occurred.
+     *
+     * @return certificate is the X.509 certificate chain for the credentialKey
+     */
+    getAttestationCertificate(vec<uint8_t> attestationChallenge)
+        generates(Result result, vec<uint8_t> certificate);
+
+    /**
+     * Start the personalization process.
+     *
+     * startPersonalization must not be called more than once.
+     *
+     * @param accessControlProfileCount specifies the number of access control profiles that will be
+     *     provisioned with addAccessControlProfile().
+     *
+     * @param entryCounts specifies the number of data entries that will be provisioned with
+     *     beginAddEntry() and addEntry(). Each item in the array specifies how many entries
+     *     will be added for each name space.
+     *
+     * @return result is OK on success, FAILED if an error occurred.
+     *
+     */
+    startPersonalization(uint16_t accessControlProfileCount, vec<uint16_t> entryCounts)
+        generates(Result result);
+
+    /**
+     * Add an access control profile, which defines the requirements or retrieval of one or more
+     * entries.  If both readerCertificate and userAuthenticationRequired are empty/false,
+     * associated entries are open access, requiring no authentication to read (though the caller
+     * is free to require other authentication above this HAL).
+     *
+     * This method must be called exactly as many times as specified in the startPersonalization()
+     * accessControlProfileCount parameter. If this is requirement is not met, the method fails
+     * with INVALID_DATA.
+     *
+     * @param id a numeric identifier that must be unique within the context of a Credential and may
+     *     be used to reference the profile. If this is not satisfied the call fails with
+     *     INVALID_DATA.
+     *
+     * @param readerCertificate if non-empty, specifies a X.509 certificate (or chain of certificates)
+     *     that must be used to authenticate requests (see the readerSignature parameter in
+     *     IIdentityCredential.startRetrieval).
+     *
+     * @param userAuthenticationRequired if true, specifies that the user is required to
+     *     authenticate to allow requests.  Required authentication freshness is specified by
+     *     timeout below.
+     *
+     * @param timeoutMillis specifies the amount of time, in milliseconds, for which a user
+     *     authentication (see userAuthenticationRequired above) is valid, if
+     *     userAuthenticationRequired is true. If the timout is zero then authentication is
+     *     required for each reader session. If userAuthenticationRequired is false, the timeout
+     *     must be zero. If this requirement is not met the call fails with INVALID_DATA.
+     *
+     * @param secureUserId must be non-zero if userAuthenticationRequired is true. It is not
+     *     related to any Android user ID or UID, but is created in the Gatekeeper application
+     *     in the secure environment. If this requirement is not met the call fails with
+     *     INVALID_DATA.
+     *
+     * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
+     *
+     * @return secureAccessControlProfile is a structure with the passed-in data and MAC created
+     *     with storageKey for authenticating the data at a later point in time.
+     */
+    addAccessControlProfile(uint16_t id, vec<uint8_t> readerCertificate,
+                            bool userAuthenticationRequired, uint64_t timeoutMillis,
+                            uint64_t secureUserId)
+        generates(Result result, SecureAccessControlProfile secureAccessControlProfile);
+
+    /**
+     * Begins the process of adding an entry to the credential.  All access control profiles must be
+     * added before calling this method.  Entries must be added in namespace "groups", meaning all
+     * entries of one namespace must be added before adding entries from another namespace.
+     *
+     * This method must be called exactly as many times as the sum of the items in the entryCounts
+     * parameter specified in the startPersonalization(), and must be followed by one or more calls
+     * to addEntryValue(). If this requirement is not met the method fails with INVALID_DATA.
+     *
+     * @param accessControlProfileIds specifies the set of access control profiles that can
+     *     authorize access to the provisioned element.
+     *
+     * @param nameSpace is the namespace of the element, e.g. "org.iso.18013"
+     *
+     * @param name is the name of the element.
+     *
+     * @param entrySize is the size of the entry value. If this requirement
+     *     is not met this method fails with INVALID_DATA.
+     *
+     * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
+     */
+    beginAddEntry(vec<uint16_t> accessControlProfileIds, string nameSpace,
+                  string name, uint32_t entrySize)
+        generates(Result result);
+
+    /**
+     * Continues the process of adding an entry, providing a value or part of a value.
+     *
+     * In the common case, this method will be called only once per entry added.  In the case of
+     * values that are larger than the secure environment's GCM chunk size
+     * (see IIdentityCredentialStore.getHardwareInformation()), the caller must provide the
+     * value in chunks.  All chunks must be exactly gcmChunkSize except the last and the sum of all
+     * chunk sizes must equal the value of the beginAddEntry() entrySize argument. If this
+     * requirement is not met the call fails with INVALID_DATA.
+     *
+     * @param content is the entry value, encoded as CBOR. In the case the content exceeds gcmChunkSize,
+     *     this may be partial content up to gcmChunkSize bytes long.
+     *
+     * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
+     *
+     * @return encryptedContent contains the encrypted and MACed content.  For directly-available
+     *     credentials the contents are implementation-defined but must not exceed 32 bytes in
+     *     length.
+     *
+     *     For other credentials, encryptedContent contains:
+     *
+     *         AES-GCM-ENC(storageKey, R, Data, AdditionalData)
+     *
+     *     where:
+     *
+     *         Data = any   ; value
+     *
+     *         AdditionalData = {
+     *             "Namespace" : tstr,
+     *             "Name" : tstr,
+     *             "AccessControlProfileIds" : [ + uint ],
+     *         }
+     */
+    addEntryValue(vec<uint8_t> content)
+        generates(Result result, vec<uint8_t> encryptedContent);
+
+    /**
+     * Finishes adding entries and returns a signature that an issuing authority may use to validate
+     * that all data was provisioned correctly.
+     *
+     * After this method is called, the IWritableIdentityCredential is no longer usable.
+     *
+     * @return result is OK on success or FAILED if an error occurred.
+     *
+     * @return credentialData is a CBOR-encoded structure (in CDDL notation):
+     *
+     *         CredentialData = [
+     *              tstr,   ; docType, an optional name that identifies the type of credential
+     *              bool,   ; testCredential, indicates if this is a test credential
+     *              bstr    ; an opaque byte vector with encrypted data, see below
+     *         ]
+     *
+     *     The last element is an opaque byte vector which contains encrypted copies of the
+     *     secrets used to secure the new credential's data and to authenticate the credential to
+     *     the issuing authority.  It contains:
+     *
+     *         AES-GCM-ENC(HBK, R, CredentialKeys, docType)
+     *
+     *     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):
+     *
+     *         CredentialKeys = [
+     *              bstr,   ; storageKey, a 128-bit AES key
+     *              bstr    ; credentialPrivKey, the private key for credentialKey
+     *         ]
+     *
+     * @return 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
+     *     startPersonalization() entryCounts parameter), it a COSE_Sign1 structure
+     *     signed by CredentialKey with payload set to the ProofOfProvisioning CBOR below:
+     *
+     *          ProofOfProvisioning = [
+     *              "ProofOfProvisioning",
+     *              tstr,                         ; DocType
+     *              [ * AccessControlProfile ],
+     *              ProvisionedData,
+     *              bool                          ; true if this is a test credential, should
+     *                                            ; always be false.
+     *          ]
+     *
+     *          AccessControlProfile = {
+     *              "id" : uint,
+     *              ? "readerCertificate" : bstr,
+     *              ? (
+     *                  "userAuthenticationRequired" : bool,
+     *                  "timeoutMillis" : uint,
+     *              )
+     *          }
+     *
+     *          ProvisionedData = {
+     *              * Namespace => [ + Entry ]
+     *          },
+     *
+     *          Namespace = tstr
+     *
+     *          Entry = {
+     *              "name" : tstr,
+     *              "value" : any,
+     *              "accessControlProfiles" : [ * uint ],
+     *          }
+     */
+    finishAddingEntries()
+        generates(Result result, vec<uint8_t> credentialData,
+                  vec<uint8_t> proofOfProvisioningSignature);
+};
diff --git a/identity/1.0/default/Android.bp b/identity/1.0/default/Android.bp
new file mode 100644
index 0000000..d2b2966
--- /dev/null
+++ b/identity/1.0/default/Android.bp
@@ -0,0 +1,43 @@
+//
+// 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.
+//
+
+cc_binary {
+    name: "android.hardware.identity@1.0-service.example",
+    init_rc: ["android.hardware.identity@1.0-service.example.rc"],
+    vendor: true,
+    relative_install_path: "hw",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+    ],
+    srcs: [
+        "service.cpp",
+        "IdentityCredential.cpp",
+        "IdentityCredentialStore.cpp",
+        "WritableIdentityCredential.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.identity@1.0",
+        "android.hardware.identity-support-lib",
+        "android.hardware.keymaster@4.0",
+        "libcppbor",
+        "libcrypto",
+        "libbase",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+}
diff --git a/identity/1.0/default/IdentityCredential.cpp b/identity/1.0/default/IdentityCredential.cpp
new file mode 100644
index 0000000..b0a5e56
--- /dev/null
+++ b/identity/1.0/default/IdentityCredential.cpp
@@ -0,0 +1,773 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IdentityCredential"
+
+#include "IdentityCredential.h"
+#include "IdentityCredentialStore.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <string.h>
+
+#include <android-base/logging.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+namespace android {
+namespace hardware {
+namespace identity {
+namespace implementation {
+
+using ::android::hardware::keymaster::V4_0::Timestamp;
+using ::std::optional;
+
+Return<void> IdentityCredential::deleteCredential(deleteCredential_cb _hidl_cb) {
+    cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
+    vector<uint8_t> proofOfDeletion = array.encode();
+
+    optional<vector<uint8_t>> proofOfDeletionSignature =
+            support::coseSignEcDsa(credentialPrivKey_,
+                                   proofOfDeletion,  // payload
+                                   {},               // additionalData
+                                   {});              // certificateChain
+    if (!proofOfDeletionSignature) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error signing data"), {});
+        return Void();
+    }
+
+    _hidl_cb(support::resultOK(), proofOfDeletionSignature.value());
+    return Void();
+}
+
+Return<void> IdentityCredential::createEphemeralKeyPair(createEphemeralKeyPair_cb _hidl_cb) {
+    optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
+    if (!keyPair) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error creating ephemeral key pair"), {});
+        return Void();
+    }
+
+    // Stash public key of this key-pair for later check in startRetrieval().
+    optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(keyPair.value());
+    if (!publicKey) {
+        _hidl_cb(support::result(ResultCode::FAILED,
+                                 "Error getting public part of ephemeral key pair"),
+                 {});
+        return Void();
+    }
+    ephemeralPublicKey_ = publicKey.value();
+
+    _hidl_cb(support::resultOK(), keyPair.value());
+    return Void();
+}
+
+Return<void> IdentityCredential::setReaderEphemeralPublicKey(
+        const hidl_vec<uint8_t>& publicKey, setReaderEphemeralPublicKey_cb _hidl_cb) {
+    readerPublicKey_ = publicKey;
+    _hidl_cb(support::resultOK());
+    return Void();
+}
+
+ResultCode IdentityCredential::initialize() {
+    auto [item, _, message] = cppbor::parse(credentialData_);
+    if (item == nullptr) {
+        LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
+        return ResultCode::INVALID_DATA;
+    }
+
+    const cppbor::Array* arrayItem = item->asArray();
+    if (arrayItem == nullptr || arrayItem->size() != 3) {
+        LOG(ERROR) << "CredentialData is not an array with three elements";
+        return ResultCode::INVALID_DATA;
+    }
+
+    const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
+    const cppbor::Bool* testCredentialItem =
+            ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
+                                                    : nullptr);
+    const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
+    if (docTypeItem == nullptr || testCredentialItem == nullptr ||
+        encryptedCredentialKeysItem == nullptr) {
+        LOG(ERROR) << "CredentialData unexpected item types";
+        return ResultCode::INVALID_DATA;
+    }
+
+    docType_ = docTypeItem->value();
+    testCredential_ = testCredentialItem->value();
+
+    vector<uint8_t> hardwareBoundKey;
+    if (testCredential_) {
+        hardwareBoundKey = support::getTestHardwareBoundKey();
+    } else {
+        hardwareBoundKey = support::getHardwareBoundKey();
+    }
+
+    const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
+    const vector<uint8_t> docTypeVec(docType_.begin(), docType_.end());
+    optional<vector<uint8_t>> decryptedCredentialKeys =
+            support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
+    if (!decryptedCredentialKeys) {
+        LOG(ERROR) << "Error decrypting CredentialKeys";
+        return ResultCode::INVALID_DATA;
+    }
+
+    auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
+    if (dckItem == nullptr) {
+        LOG(ERROR) << "Decrypted CredentialKeys is not valid CBOR: " << dckMessage;
+        return ResultCode::INVALID_DATA;
+    }
+    const cppbor::Array* dckArrayItem = dckItem->asArray();
+    if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
+        LOG(ERROR) << "Decrypted CredentialKeys is not an array with two elements";
+        return ResultCode::INVALID_DATA;
+    }
+    const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
+    const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
+    if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
+        LOG(ERROR) << "CredentialKeys unexpected item types";
+        return ResultCode::INVALID_DATA;
+    }
+    storageKey_ = storageKeyItem->value();
+    credentialPrivKey_ = credentialPrivKeyItem->value();
+
+    return ResultCode::OK;
+}
+
+Return<void> IdentityCredential::createAuthChallenge(createAuthChallenge_cb _hidl_cb) {
+    uint64_t challenge = 0;
+    while (challenge == 0) {
+        optional<vector<uint8_t>> bytes = support::getRandom(8);
+        if (!bytes) {
+            _hidl_cb(support::result(ResultCode::FAILED, "Error getting random data for challenge"),
+                     0);
+            return Void();
+        }
+
+        challenge = 0;
+        for (size_t n = 0; n < bytes.value().size(); n++) {
+            challenge |= ((bytes.value())[n] << (n * 8));
+        }
+    }
+
+    authChallenge_ = challenge;
+    _hidl_cb(support::resultOK(), challenge);
+    return Void();
+}
+
+// TODO: this could be a lot faster if we did all the splitting and pubkey extraction
+// ahead of time.
+bool checkReaderAuthentication(const SecureAccessControlProfile& profile,
+                               const vector<uint8_t>& readerCertificateChain) {
+    optional<vector<uint8_t>> acpPubKey =
+            support::certificateChainGetTopMostKey(profile.readerCertificate);
+    if (!acpPubKey) {
+        LOG(ERROR) << "Error extracting public key from readerCertificate in profile";
+        return false;
+    }
+
+    optional<vector<vector<uint8_t>>> certificatesInChain =
+            support::certificateChainSplit(readerCertificateChain);
+    if (!certificatesInChain) {
+        LOG(ERROR) << "Error splitting readerCertificateChain";
+        return false;
+    }
+    for (const vector<uint8_t>& certInChain : certificatesInChain.value()) {
+        optional<vector<uint8_t>> certPubKey = support::certificateChainGetTopMostKey(certInChain);
+        if (!certPubKey) {
+            LOG(ERROR)
+                    << "Error extracting public key from certificate in chain presented by reader";
+            return false;
+        }
+        if (acpPubKey.value() == certPubKey.value()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+Timestamp clockGetTime() {
+    struct timespec time;
+    clock_gettime(CLOCK_MONOTONIC, &time);
+    return time.tv_sec * 1000 + time.tv_nsec / 1000000;
+}
+
+bool checkUserAuthentication(const SecureAccessControlProfile& profile,
+                             const HardwareAuthToken& authToken, uint64_t authChallenge) {
+    if (profile.secureUserId != authToken.userId) {
+        LOG(ERROR) << "secureUserId in profile (" << profile.secureUserId
+                   << ") differs from userId in authToken (" << authToken.userId << ")";
+        return false;
+    }
+
+    if (profile.timeoutMillis == 0) {
+        if (authToken.challenge == 0) {
+            LOG(ERROR) << "No challenge in authToken";
+            return false;
+        }
+
+        if (authToken.challenge != authChallenge) {
+            LOG(ERROR) << "Challenge in authToken doesn't match the challenge we created";
+            return false;
+        }
+        return true;
+    }
+
+    // Note that the Epoch for timestamps in HardwareAuthToken is at the
+    // discretion of the vendor:
+    //
+    //   "[...] since some starting point (generally the most recent device
+    //    boot) which all of the applications within one secure environment
+    //    must agree upon."
+    //
+    // Therefore, if this software implementation is used on a device which isn't
+    // the emulator then the assumption that the epoch is the same as used in
+    // clockGetTime above will not hold. This is OK as this software
+    // implementation should never be used on a real device.
+    //
+    Timestamp now = clockGetTime();
+    if (authToken.timestamp > now) {
+        LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp
+                   << ") is in the future (now: " << now << ")";
+        return false;
+    }
+    if (now > authToken.timestamp + profile.timeoutMillis) {
+        LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp << " + "
+                   << profile.timeoutMillis << " = "
+                   << (authToken.timestamp + profile.timeoutMillis)
+                   << ") is in the past (now: " << now << ")";
+        return false;
+    }
+
+    return true;
+}
+
+Return<void> IdentityCredential::startRetrieval(
+        const hidl_vec<SecureAccessControlProfile>& accessControlProfiles,
+        const HardwareAuthToken& authToken, const hidl_vec<uint8_t>& itemsRequest,
+        const hidl_vec<uint8_t>& sessionTranscript, const hidl_vec<uint8_t>& readerSignature,
+        const hidl_vec<uint16_t>& requestCounts, startRetrieval_cb _hidl_cb) {
+    if (sessionTranscript.size() > 0) {
+        auto [item, _, message] = cppbor::parse(sessionTranscript);
+        if (item == nullptr) {
+            _hidl_cb(support::result(ResultCode::INVALID_DATA,
+                                     "SessionTranscript contains invalid CBOR"));
+            return Void();
+        }
+        sessionTranscriptItem_ = std::move(item);
+    }
+    if (numStartRetrievalCalls_ > 0) {
+        if (sessionTranscript_ != vector<uint8_t>(sessionTranscript)) {
+            _hidl_cb(support::result(
+                    ResultCode::SESSION_TRANSCRIPT_MISMATCH,
+                    "Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
+            return Void();
+        }
+    }
+    sessionTranscript_ = sessionTranscript;
+
+    // If there is a signature, validate that it was made with the top-most key in the
+    // certificate chain embedded in the COSE_Sign1 structure.
+    optional<vector<uint8_t>> readerCertificateChain;
+    if (readerSignature.size() > 0) {
+        readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
+        if (!readerCertificateChain) {
+            _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
+                                     "Unable to get reader certificate chain from COSE_Sign1"));
+            return Void();
+        }
+
+        if (!support::certificateChainValidate(readerCertificateChain.value())) {
+            _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
+                                     "Error validating reader certificate chain"));
+            return Void();
+        }
+
+        optional<vector<uint8_t>> readerPublicKey =
+                support::certificateChainGetTopMostKey(readerCertificateChain.value());
+        if (!readerPublicKey) {
+            _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
+                                     "Unable to get public key from reader certificate chain"));
+            return Void();
+        }
+
+        const vector<uint8_t>& itemsRequestBytes = itemsRequest;
+        vector<uint8_t> dataThatWasSigned = cppbor::Array()
+                                                    .add("ReaderAuthentication")
+                                                    .add(sessionTranscriptItem_->clone())
+                                                    .add(cppbor::Semantic(24, itemsRequestBytes))
+                                                    .encode();
+        if (!support::coseCheckEcDsaSignature(readerSignature,
+                                              dataThatWasSigned,  // detached content
+                                              readerPublicKey.value())) {
+            _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
+                                     "readerSignature check failed"));
+            return Void();
+        }
+    }
+
+    // Here's where we would validate the passed-in |authToken| to assure ourselves
+    // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
+    //
+    // However this involves calculating the MAC. However this requires access
+    // to the key needed to a pre-shared key which we don't have...
+    //
+
+    // To prevent replay-attacks, we check that the public part of the ephemeral
+    // key we previously created, is present in the DeviceEngagement part of
+    // SessionTranscript as a COSE_Key, in uncompressed form.
+    //
+    // We do this by just searching for the X and Y coordinates.
+    if (sessionTranscript.size() > 0) {
+        const cppbor::Array* array = sessionTranscriptItem_->asArray();
+        if (array == nullptr || array->size() != 2) {
+            _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+                                     "SessionTranscript is not an array with two items"));
+            return Void();
+        }
+        const cppbor::Semantic* taggedEncodedDE = (*array)[0]->asSemantic();
+        if (taggedEncodedDE == nullptr || taggedEncodedDE->value() != 24) {
+            _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+                                     "First item in SessionTranscript array is not a "
+                                     "semantic with value 24"));
+            return Void();
+        }
+        const cppbor::Bstr* encodedDE = (taggedEncodedDE->child())->asBstr();
+        if (encodedDE == nullptr) {
+            _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+                                     "Child of semantic in first item in SessionTranscript "
+                                     "array is not a bstr"));
+            return Void();
+        }
+        const vector<uint8_t>& bytesDE = encodedDE->value();
+
+        auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
+        if (!getXYSuccess) {
+            _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+                                     "Error extracting X and Y from ePub"));
+            return Void();
+        }
+        if (sessionTranscript.size() > 0 &&
+            !(memmem(bytesDE.data(), bytesDE.size(), ePubX.data(), ePubX.size()) != nullptr &&
+              memmem(bytesDE.data(), bytesDE.size(), ePubY.data(), ePubY.size()) != nullptr)) {
+            _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+                                     "Did not find ephemeral public key's X and Y coordinates in "
+                                     "SessionTranscript (make sure leading zeroes are not used)"));
+            return Void();
+        }
+    }
+
+    // itemsRequest: If non-empty, contains request data that may be signed by the
+    // reader.  The content can be defined in the way appropriate for the
+    // credential, but there are three requirements that must be met to work with
+    // this HAL:
+    if (itemsRequest.size() > 0) {
+        // 1. The content must be a CBOR-encoded structure.
+        auto [item, _, message] = cppbor::parse(itemsRequest);
+        if (item == nullptr) {
+            _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
+                                     "Error decoding CBOR in itemsRequest: %s", message.c_str()));
+            return Void();
+        }
+
+        // 2. The CBOR structure must be a map.
+        const cppbor::Map* map = item->asMap();
+        if (map == nullptr) {
+            _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
+                                     "itemsRequest is not a CBOR map"));
+            return Void();
+        }
+
+        // 3. The map must contain a key "nameSpaces" whose value contains a map, as described in
+        //    the example below.
+        //
+        //   NameSpaces = {
+        //     + NameSpace => DataElements ; Requested data elements for each NameSpace
+        //   }
+        //
+        //   NameSpace = tstr
+        //
+        //   DataElements = {
+        //     + DataElement => IntentToRetain
+        //   }
+        //
+        //   DataElement = tstr
+        //   IntentToRetain = bool
+        //
+        // Here's an example of an |itemsRequest| CBOR value satisfying above requirements 1.
+        // through 3.:
+        //
+        //    {
+        //        'docType' : 'org.iso.18013-5.2019',
+        //        'nameSpaces' : {
+        //            'org.iso.18013-5.2019' : {
+        //                'Last name' : false,
+        //                'Birth date' : false,
+        //                'First name' : false,
+        //                'Home address' : true
+        //            },
+        //            'org.aamva.iso.18013-5.2019' : {
+        //                'Real Id' : false
+        //            }
+        //        }
+        //    }
+        //
+        const cppbor::Map* nsMap = nullptr;
+        for (size_t n = 0; n < map->size(); n++) {
+            const auto& [keyItem, valueItem] = (*map)[n];
+            if (keyItem->type() == cppbor::TSTR && keyItem->asTstr()->value() == "nameSpaces" &&
+                valueItem->type() == cppbor::MAP) {
+                nsMap = valueItem->asMap();
+                break;
+            }
+        }
+        if (nsMap == nullptr) {
+            _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
+                                     "No nameSpaces map in top-most map"));
+            return Void();
+        }
+
+        for (size_t n = 0; n < nsMap->size(); n++) {
+            auto [nsKeyItem, nsValueItem] = (*nsMap)[n];
+            const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
+            const cppbor::Map* nsInnerMap = nsValueItem->asMap();
+            if (nsKey == nullptr || nsInnerMap == nullptr) {
+                _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
+                                         "Type mismatch in nameSpaces map"));
+                return Void();
+            }
+            string requestedNamespace = nsKey->value();
+            vector<string> requestedKeys;
+            for (size_t m = 0; m < nsInnerMap->size(); m++) {
+                const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m];
+                const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr();
+                const cppbor::Simple* simple = innerMapValueItem->asSimple();
+                const cppbor::Bool* intentToRetainItem =
+                        (simple != nullptr) ? simple->asBool() : nullptr;
+                if (nameItem == nullptr || intentToRetainItem == nullptr) {
+                    _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
+                                             "Type mismatch in value in nameSpaces map"));
+                    return Void();
+                }
+                requestedKeys.push_back(nameItem->value());
+            }
+            requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys;
+        }
+    }
+
+    // Finally, validate all the access control profiles in the requestData.
+    bool haveAuthToken = (authToken.mac.size() > 0);
+    for (const auto& profile : accessControlProfiles) {
+        if (!support::secureAccessControlProfileCheckMac(profile, storageKey_)) {
+            _hidl_cb(support::result(ResultCode::INVALID_DATA,
+                                     "Error checking MAC for profile with id %d", int(profile.id)));
+            return Void();
+        }
+        ResultCode accessControlCheck = ResultCode::OK;
+        if (profile.userAuthenticationRequired) {
+            if (!haveAuthToken || !checkUserAuthentication(profile, authToken, authChallenge_)) {
+                accessControlCheck = ResultCode::USER_AUTHENTICATION_FAILED;
+            }
+        } else if (profile.readerCertificate.size() > 0) {
+            if (!readerCertificateChain ||
+                !checkReaderAuthentication(profile, readerCertificateChain.value())) {
+                accessControlCheck = ResultCode::READER_AUTHENTICATION_FAILED;
+            }
+        }
+        profileIdToAccessCheckResult_[profile.id] = accessControlCheck;
+    }
+
+    deviceNameSpacesMap_ = cppbor::Map();
+    currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
+
+    requestCountsRemaining_ = requestCounts;
+    currentNameSpace_ = "";
+
+    itemsRequest_ = itemsRequest;
+
+    numStartRetrievalCalls_ += 1;
+    _hidl_cb(support::resultOK());
+    return Void();
+}
+
+Return<void> IdentityCredential::startRetrieveEntryValue(
+        const hidl_string& nameSpace, const hidl_string& name, uint32_t entrySize,
+        const hidl_vec<uint16_t>& accessControlProfileIds, startRetrieveEntryValue_cb _hidl_cb) {
+    if (name.empty()) {
+        _hidl_cb(support::result(ResultCode::INVALID_DATA, "Name cannot be empty"));
+        return Void();
+    }
+    if (nameSpace.empty()) {
+        _hidl_cb(support::result(ResultCode::INVALID_DATA, "Name space cannot be empty"));
+        return Void();
+    }
+
+    if (requestCountsRemaining_.size() == 0) {
+        _hidl_cb(support::result(ResultCode::INVALID_DATA,
+                                 "No more name spaces left to go through"));
+        return Void();
+    }
+
+    if (currentNameSpace_ == "") {
+        // First call.
+        currentNameSpace_ = nameSpace;
+    }
+
+    if (nameSpace == currentNameSpace_) {
+        // Same namespace.
+        if (requestCountsRemaining_[0] == 0) {
+            _hidl_cb(support::result(ResultCode::INVALID_DATA,
+                                     "No more entries to be retrieved in current name space"));
+            return Void();
+        }
+        requestCountsRemaining_[0] -= 1;
+    } else {
+        // New namespace.
+        if (requestCountsRemaining_[0] != 0) {
+            _hidl_cb(support::result(ResultCode::INVALID_DATA,
+                                     "Moved to new name space but %d entries need to be retrieved "
+                                     "in current name space",
+                                     int(requestCountsRemaining_[0])));
+            return Void();
+        }
+        if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
+            deviceNameSpacesMap_.add(currentNameSpace_,
+                                     std::move(currentNameSpaceDeviceNameSpacesMap_));
+        }
+        currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
+
+        requestCountsRemaining_.erase(requestCountsRemaining_.begin());
+        currentNameSpace_ = nameSpace;
+    }
+
+    // It's permissible to have an empty itemsRequest... but if non-empty you can
+    // only request what was specified in said itemsRequest. Enforce that.
+    if (itemsRequest_.size() > 0) {
+        const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
+        if (it == requestedNameSpacesAndNames_.end()) {
+            _hidl_cb(support::result(ResultCode::NOT_IN_REQUEST_MESSAGE,
+                                     "Name space '%s' was not requested in startRetrieval",
+                                     nameSpace.c_str()));
+            return Void();
+        }
+        const auto& dataItemNames = it->second;
+        if (std::find(dataItemNames.begin(), dataItemNames.end(), name) == dataItemNames.end()) {
+            _hidl_cb(support::result(
+                    ResultCode::NOT_IN_REQUEST_MESSAGE,
+                    "Data item name '%s' in name space '%s' was not requested in startRetrieval",
+                    name.c_str(), nameSpace.c_str()));
+            return Void();
+        }
+    }
+
+    // Enforce access control.
+    //
+    // Access is granted if at least one of the profiles grants access.
+    //
+    // If an item is configured without any profiles, access is denied.
+    //
+    ResultCode accessControl = ResultCode::NO_ACCESS_CONTROL_PROFILES;
+    for (auto id : accessControlProfileIds) {
+        auto search = profileIdToAccessCheckResult_.find(id);
+        if (search == profileIdToAccessCheckResult_.end()) {
+            _hidl_cb(support::result(ResultCode::INVALID_DATA,
+                                     "Requested entry with unvalidated profile id %d", (int(id))));
+            return Void();
+        }
+        ResultCode accessControlForProfile = search->second;
+        if (accessControlForProfile == ResultCode::OK) {
+            accessControl = ResultCode::OK;
+            break;
+        }
+        accessControl = accessControlForProfile;
+    }
+    if (accessControl != ResultCode::OK) {
+        _hidl_cb(support::result(accessControl, "Access control check failed"));
+        return Void();
+    }
+
+    entryAdditionalData_ =
+            support::entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
+
+    currentName_ = name;
+    entryRemainingBytes_ = entrySize;
+    entryValue_.resize(0);
+
+    _hidl_cb(support::resultOK());
+    return Void();
+}
+
+Return<void> IdentityCredential::retrieveEntryValue(const hidl_vec<uint8_t>& encryptedContent,
+                                                    retrieveEntryValue_cb _hidl_cb) {
+    optional<vector<uint8_t>> content =
+            support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_);
+    if (!content) {
+        _hidl_cb(support::result(ResultCode::INVALID_DATA, "Error decrypting data"), {});
+        return Void();
+    }
+
+    size_t chunkSize = content.value().size();
+
+    if (chunkSize > entryRemainingBytes_) {
+        LOG(ERROR) << "Retrieved chunk of size " << chunkSize
+                   << " is bigger than remaining space of size " << entryRemainingBytes_;
+        _hidl_cb(support::result(ResultCode::INVALID_DATA,
+                                 "Retrieved chunk of size %zd is bigger than remaining space "
+                                 "of size %zd",
+                                 chunkSize, entryRemainingBytes_),
+                 {});
+        return Void();
+    }
+
+    entryRemainingBytes_ -= chunkSize;
+    if (entryRemainingBytes_ > 0) {
+        if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
+            _hidl_cb(support::result(ResultCode::INVALID_DATA,
+                                     "Retrieved non-final chunk of size %zd but expected "
+                                     "kGcmChunkSize which is %zd",
+                                     chunkSize, IdentityCredentialStore::kGcmChunkSize),
+                     {});
+            return Void();
+        }
+    }
+
+    entryValue_.insert(entryValue_.end(), content.value().begin(), content.value().end());
+
+    if (entryRemainingBytes_ == 0) {
+        auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
+        if (entryValueItem == nullptr) {
+            _hidl_cb(support::result(ResultCode::INVALID_DATA, "Retrieved data invalid CBOR"), {});
+            return Void();
+        }
+        currentNameSpaceDeviceNameSpacesMap_.add(currentName_, std::move(entryValueItem));
+    }
+
+    _hidl_cb(support::resultOK(), content.value());
+    return Void();
+}
+
+Return<void> IdentityCredential::finishRetrieval(const hidl_vec<uint8_t>& signingKeyBlob,
+                                                 finishRetrieval_cb _hidl_cb) {
+    if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
+        deviceNameSpacesMap_.add(currentNameSpace_,
+                                 std::move(currentNameSpaceDeviceNameSpacesMap_));
+    }
+    vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode();
+
+    // If there's no signing key or no sessionTranscript or no reader ephemeral
+    // public key, we return the empty MAC.
+    optional<vector<uint8_t>> mac;
+    if (signingKeyBlob.size() > 0 && sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0) {
+        cppbor::Array array;
+        array.add("DeviceAuthentication");
+        array.add(sessionTranscriptItem_->clone());
+        array.add(docType_);
+        array.add(cppbor::Semantic(24, encodedDeviceNameSpaces));
+        vector<uint8_t> encodedDeviceAuthentication = array.encode();
+
+        vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
+        optional<vector<uint8_t>> signingKey =
+                support::decryptAes128Gcm(storageKey_, signingKeyBlob, docTypeAsBlob);
+        if (!signingKey) {
+            _hidl_cb(support::result(ResultCode::INVALID_DATA, "Error decrypting signingKeyBlob"),
+                     {}, {});
+            return Void();
+        }
+
+        optional<vector<uint8_t>> sharedSecret =
+                support::ecdh(readerPublicKey_, signingKey.value());
+        if (!sharedSecret) {
+            _hidl_cb(support::result(ResultCode::FAILED, "Error doing ECDH"), {}, {});
+            return Void();
+        }
+
+        vector<uint8_t> salt = {0x00};
+        vector<uint8_t> info = {};
+        optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
+        if (!derivedKey) {
+            _hidl_cb(support::result(ResultCode::FAILED, "Error deriving key from shared secret"),
+                     {}, {});
+            return Void();
+        }
+
+        mac = support::coseMac0(derivedKey.value(), {},        // payload
+                                encodedDeviceAuthentication);  // additionalData
+        if (!mac) {
+            _hidl_cb(support::result(ResultCode::FAILED, "Error MACing data"), {}, {});
+            return Void();
+        }
+    }
+
+    _hidl_cb(support::resultOK(), mac.value_or(vector<uint8_t>({})), encodedDeviceNameSpaces);
+    return Void();
+}
+
+Return<void> IdentityCredential::generateSigningKeyPair(generateSigningKeyPair_cb _hidl_cb) {
+    string serialDecimal = "0";  // TODO: set serial to something unique
+    string issuer = "Android Open Source Project";
+    string subject = "Android IdentityCredential Reference Implementation";
+    time_t validityNotBefore = time(nullptr);
+    time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
+
+    optional<vector<uint8_t>> signingKeyPKCS8 = support::createEcKeyPair();
+    if (!signingKeyPKCS8) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error creating signingKey"), {}, {});
+        return Void();
+    }
+
+    optional<vector<uint8_t>> signingPublicKey =
+            support::ecKeyPairGetPublicKey(signingKeyPKCS8.value());
+    if (!signingPublicKey) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error getting public part of signingKey"), {},
+                 {});
+        return Void();
+    }
+
+    optional<vector<uint8_t>> signingKey = support::ecKeyPairGetPrivateKey(signingKeyPKCS8.value());
+    if (!signingKey) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error getting private part of signingKey"),
+                 {}, {});
+        return Void();
+    }
+
+    optional<vector<uint8_t>> certificate = support::ecPublicKeyGenerateCertificate(
+            signingPublicKey.value(), credentialPrivKey_, serialDecimal, issuer, subject,
+            validityNotBefore, validityNotAfter);
+    if (!certificate) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error creating signingKey"), {}, {});
+        return Void();
+    }
+
+    optional<vector<uint8_t>> nonce = support::getRandom(12);
+    if (!nonce) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error getting random"), {}, {});
+        return Void();
+    }
+    vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
+    optional<vector<uint8_t>> encryptedSigningKey = support::encryptAes128Gcm(
+            storageKey_, nonce.value(), signingKey.value(), docTypeAsBlob);
+    if (!encryptedSigningKey) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error encrypting signingKey"), {}, {});
+        return Void();
+    }
+    _hidl_cb(support::resultOK(), encryptedSigningKey.value(), certificate.value());
+    return Void();
+}
+
+}  // namespace implementation
+}  // namespace identity
+}  // namespace hardware
+}  // namespace android
diff --git a/identity/1.0/default/IdentityCredential.h b/identity/1.0/default/IdentityCredential.h
new file mode 100644
index 0000000..eb8787b
--- /dev/null
+++ b/identity/1.0/default/IdentityCredential.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
+#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
+
+#include <android/hardware/identity/1.0/IIdentityCredential.h>
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <cppbor/cppbor.h>
+
+namespace android {
+namespace hardware {
+namespace identity {
+namespace implementation {
+
+using ::std::map;
+using ::std::string;
+using ::std::vector;
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::identity::V1_0::IIdentityCredential;
+using ::android::hardware::identity::V1_0::Result;
+using ::android::hardware::identity::V1_0::ResultCode;
+using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
+using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+
+using MapStringToVectorOfStrings = map<string, vector<string>>;
+
+class IdentityCredential : public IIdentityCredential {
+  public:
+    IdentityCredential(const hidl_vec<uint8_t>& credentialData)
+        : credentialData_(credentialData), numStartRetrievalCalls_(0), authChallenge_(0) {}
+
+    // Parses and decrypts credentialData_, return false on failure. Must be
+    // called right after construction.
+    ResultCode initialize();
+
+    // Methods from ::android::hardware::identity::IIdentityCredential follow.
+
+    Return<void> deleteCredential(deleteCredential_cb _hidl_cb) override;
+    Return<void> createEphemeralKeyPair(createEphemeralKeyPair_cb _hidl_cb) override;
+
+    Return<void> setReaderEphemeralPublicKey(const hidl_vec<uint8_t>& publicKey,
+                                             setReaderEphemeralPublicKey_cb _hidl_cb) override;
+
+    Return<void> createAuthChallenge(createAuthChallenge_cb _hidl_cb) override;
+
+    Return<void> startRetrieval(const hidl_vec<SecureAccessControlProfile>& accessControlProfiles,
+                                const HardwareAuthToken& authToken,
+                                const hidl_vec<uint8_t>& itemsRequest,
+                                const hidl_vec<uint8_t>& sessionTranscript,
+                                const hidl_vec<uint8_t>& readerSignature,
+                                const hidl_vec<uint16_t>& requestCounts,
+                                startRetrieval_cb _hidl_cb) override;
+    Return<void> startRetrieveEntryValue(const hidl_string& nameSpace, const hidl_string& name,
+                                         uint32_t entrySize,
+                                         const hidl_vec<uint16_t>& accessControlProfileIds,
+                                         startRetrieveEntryValue_cb _hidl_cb) override;
+    Return<void> retrieveEntryValue(const hidl_vec<uint8_t>& encryptedContent,
+                                    retrieveEntryValue_cb _hidl_cb) override;
+    Return<void> finishRetrieval(const hidl_vec<uint8_t>& signingKeyBlob,
+                                 finishRetrieval_cb _hidl_cb) override;
+
+    Return<void> generateSigningKeyPair(generateSigningKeyPair_cb _hidl_cb) override;
+
+  private:
+    // Set by constructor
+    vector<uint8_t> credentialData_;
+    int numStartRetrievalCalls_;
+
+    // Set by initialize()
+    string docType_;
+    bool testCredential_;
+    vector<uint8_t> storageKey_;
+    vector<uint8_t> credentialPrivKey_;
+
+    // Set by createEphemeralKeyPair()
+    vector<uint8_t> ephemeralPublicKey_;
+
+    // Set by setReaderEphemeralPublicKey()
+    vector<uint8_t> readerPublicKey_;
+
+    // Set by createAuthChallenge()
+    uint64_t authChallenge_;
+
+    // Set at startRetrieval() time.
+    map<uint16_t, ResultCode> profileIdToAccessCheckResult_;
+    vector<uint8_t> sessionTranscript_;
+    std::unique_ptr<cppbor::Item> sessionTranscriptItem_;
+    vector<uint8_t> itemsRequest_;
+    vector<uint16_t> requestCountsRemaining_;
+    MapStringToVectorOfStrings requestedNameSpacesAndNames_;
+    cppbor::Map deviceNameSpacesMap_;
+    cppbor::Map currentNameSpaceDeviceNameSpacesMap_;
+
+    // Set at startRetrieveEntryValue() time.
+    string currentNameSpace_;
+    string currentName_;
+    size_t entryRemainingBytes_;
+    vector<uint8_t> entryValue_;
+    vector<uint8_t> entryAdditionalData_;
+};
+
+}  // namespace implementation
+}  // namespace identity
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
diff --git a/identity/1.0/default/IdentityCredentialStore.cpp b/identity/1.0/default/IdentityCredentialStore.cpp
new file mode 100644
index 0000000..9eb1e70
--- /dev/null
+++ b/identity/1.0/default/IdentityCredentialStore.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IdentityCredentialStore"
+
+#include "IdentityCredentialStore.h"
+#include "IdentityCredential.h"
+#include "WritableIdentityCredential.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace hardware {
+namespace identity {
+namespace implementation {
+
+// Methods from ::android::hardware::identity::IIdentityCredentialStore follow.
+
+Return<void> IdentityCredentialStore::getHardwareInformation(getHardwareInformation_cb _hidl_cb) {
+    _hidl_cb(support::resultOK(), "IdentityCredential Reference Implementation", "Google",
+             kGcmChunkSize, false /* isDirectAccess */, {} /* supportedDocTypes */);
+    return Void();
+}
+
+Return<void> IdentityCredentialStore::createCredential(const hidl_string& docType,
+                                                       bool testCredential,
+                                                       createCredential_cb _hidl_cb) {
+    auto writable_credential = new WritableIdentityCredential(docType, testCredential);
+    if (!writable_credential->initialize()) {
+        _hidl_cb(support::result(ResultCode::FAILED,
+                                 "Error initializing WritableIdentityCredential"),
+                 writable_credential);
+        return Void();
+    }
+    _hidl_cb(support::resultOK(), writable_credential);
+    return Void();
+}
+
+Return<void> IdentityCredentialStore::getCredential(const hidl_vec<uint8_t>& credentialData,
+                                                    getCredential_cb _hidl_cb) {
+    auto credential = new IdentityCredential(credentialData);
+    // We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right now.
+    auto ret = credential->initialize();
+    if (ret != ResultCode::OK) {
+        _hidl_cb(support::result(ret, "Error initializing IdentityCredential"), credential);
+        return Void();
+    }
+    _hidl_cb(support::resultOK(), credential);
+    return Void();
+}
+
+}  // namespace implementation
+}  // namespace identity
+}  // namespace hardware
+}  // namespace android
diff --git a/identity/1.0/default/IdentityCredentialStore.h b/identity/1.0/default/IdentityCredentialStore.h
new file mode 100644
index 0000000..ad75360
--- /dev/null
+++ b/identity/1.0/default/IdentityCredentialStore.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
+#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
+
+#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
+
+namespace android {
+namespace hardware {
+namespace identity {
+namespace implementation {
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
+using ::android::hardware::identity::V1_0::Result;
+using ::android::hardware::identity::V1_0::ResultCode;
+
+class IdentityCredentialStore : public IIdentityCredentialStore {
+  public:
+    IdentityCredentialStore() {}
+
+    // The GCM chunk size used by this implementation is 64 KiB.
+    static constexpr size_t kGcmChunkSize = 64 * 1024;
+
+    // Methods from ::android::hardware::identity::IIdentityCredentialStore follow.
+    Return<void> getHardwareInformation(getHardwareInformation_cb _hidl_cb) override;
+    Return<void> createCredential(const hidl_string& docType, bool testCredential,
+                                  createCredential_cb _hidl_cb) override;
+    Return<void> getCredential(const hidl_vec<uint8_t>& credentialData,
+                               getCredential_cb _hidl_cb) override;
+};
+
+}  // namespace implementation
+}  // namespace identity
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
diff --git a/identity/1.0/default/OWNERS b/identity/1.0/default/OWNERS
new file mode 100644
index 0000000..6969910
--- /dev/null
+++ b/identity/1.0/default/OWNERS
@@ -0,0 +1,2 @@
+swillden@google.com
+zeuthen@google.com
diff --git a/identity/1.0/default/WritableIdentityCredential.cpp b/identity/1.0/default/WritableIdentityCredential.cpp
new file mode 100644
index 0000000..548b4c0
--- /dev/null
+++ b/identity/1.0/default/WritableIdentityCredential.cpp
@@ -0,0 +1,426 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "WritableIdentityCredential"
+
+#include "WritableIdentityCredential.h"
+#include "IdentityCredentialStore.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <android-base/logging.h>
+
+#include <cppbor/cppbor.h>
+#include <cppbor/cppbor_parse.h>
+
+namespace android {
+namespace hardware {
+namespace identity {
+namespace implementation {
+
+using ::std::optional;
+
+// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and
+// |credentialPrivKey|.
+static bool generateCredentialKeys(const vector<uint8_t>& storageKey,
+                                   const vector<uint8_t>& credentialPrivKey,
+                                   vector<uint8_t>& credentialKeys) {
+    if (storageKey.size() != 16) {
+        LOG(ERROR) << "Size of storageKey is not 16";
+        return false;
+    }
+
+    cppbor::Array array;
+    array.add(cppbor::Bstr(storageKey));
+    array.add(cppbor::Bstr(credentialPrivKey));
+    credentialKeys = array.encode();
+    return true;
+}
+
+// Writes CBOR-encoded structure to |credentialData| containing |docType|,
+// |testCredential| and |credentialKeys|. The latter element will be stored in
+// encrypted form, using |hardwareBoundKey| as the encryption key.
+bool generateCredentialData(const vector<uint8_t>& hardwareBoundKey, const string& docType,
+                            bool testCredential, const vector<uint8_t>& credentialKeys,
+                            vector<uint8_t>& credentialData) {
+    optional<vector<uint8_t>> nonce = support::getRandom(12);
+    if (!nonce) {
+        LOG(ERROR) << "Error getting random";
+        return false;
+    }
+    vector<uint8_t> docTypeAsVec(docType.begin(), docType.end());
+    optional<vector<uint8_t>> credentialBlob = support::encryptAes128Gcm(
+            hardwareBoundKey, nonce.value(), credentialKeys, docTypeAsVec);
+    if (!credentialBlob) {
+        LOG(ERROR) << "Error encrypting CredentialKeys blob";
+        return false;
+    }
+
+    cppbor::Array array;
+    array.add(docType);
+    array.add(testCredential);
+    array.add(cppbor::Bstr(credentialBlob.value()));
+    credentialData = array.encode();
+    return true;
+}
+
+bool WritableIdentityCredential::initialize() {
+    optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
+    if (!keyPair) {
+        LOG(ERROR) << "Error creating credentialKey";
+        return false;
+    }
+
+    optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
+    if (!pubKey) {
+        LOG(ERROR) << "Error getting public part of credentialKey";
+        return false;
+    }
+    credentialPubKey_ = pubKey.value();
+
+    optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
+    if (!privKey) {
+        LOG(ERROR) << "Error getting private part of credentialKey";
+        return false;
+    }
+    credentialPrivKey_ = privKey.value();
+
+    optional<vector<uint8_t>> random = support::getRandom(16);
+    if (!random) {
+        LOG(ERROR) << "Error creating storageKey";
+        return false;
+    }
+    storageKey_ = random.value();
+
+    return true;
+}
+
+Return<void> WritableIdentityCredential::getAttestationCertificate(
+        const hidl_vec<uint8_t>& /* attestationChallenge */,
+        getAttestationCertificate_cb _hidl_cb) {
+    // For now, we dynamically generate an attestion key on each and every
+    // request and use that to sign CredentialKey. In a real implementation this
+    // would look very differently.
+    optional<vector<uint8_t>> attestationKeyPair = support::createEcKeyPair();
+    if (!attestationKeyPair) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error creating attestationKey"), {});
+        return Void();
+    }
+
+    optional<vector<uint8_t>> attestationPubKey =
+            support::ecKeyPairGetPublicKey(attestationKeyPair.value());
+    if (!attestationPubKey) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error getting public part of attestationKey"),
+                 {});
+        return Void();
+    }
+
+    optional<vector<uint8_t>> attestationPrivKey =
+            support::ecKeyPairGetPrivateKey(attestationKeyPair.value());
+    if (!attestationPrivKey) {
+        _hidl_cb(
+                support::result(ResultCode::FAILED, "Error getting private part of attestationKey"),
+                {});
+        return Void();
+    }
+
+    string serialDecimal;
+    string issuer;
+    string subject;
+    time_t validityNotBefore = time(nullptr);
+    time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
+
+    // First create a certificate for |credentialPubKey| which is signed by
+    // |attestationPrivKey|.
+    //
+    serialDecimal = "0";  // TODO: set serial to |attestationChallenge|
+    issuer = "Android Open Source Project";
+    subject = "Android IdentityCredential CredentialKey";
+    optional<vector<uint8_t>> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate(
+            credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject,
+            validityNotBefore, validityNotAfter);
+    if (!credentialPubKeyCertificate) {
+        _hidl_cb(support::result(ResultCode::FAILED,
+                                 "Error creating certificate for credentialPubKey"),
+                 {});
+        return Void();
+    }
+
+    // This is followed by a certificate for |attestationPubKey| self-signed by
+    // |attestationPrivKey|.
+    serialDecimal = "0";  // TODO: set serial
+    issuer = "Android Open Source Project";
+    subject = "Android IdentityCredential AttestationKey";
+    optional<vector<uint8_t>> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate(
+            attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject,
+            validityNotBefore, validityNotAfter);
+    if (!attestationKeyCertificate) {
+        _hidl_cb(support::result(ResultCode::FAILED,
+                                 "Error creating certificate for attestationPubKey"),
+                 {});
+        return Void();
+    }
+
+    // Concatenate the certificates to form the chain.
+    vector<uint8_t> certificateChain;
+    certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(),
+                            credentialPubKeyCertificate.value().end());
+    certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(),
+                            attestationKeyCertificate.value().end());
+
+    _hidl_cb(support::resultOK(), certificateChain);
+    return Void();
+}
+
+Return<void> WritableIdentityCredential::startPersonalization(uint16_t accessControlProfileCount,
+                                                              const hidl_vec<uint16_t>& entryCounts,
+                                                              startPersonalization_cb _hidl_cb) {
+    numAccessControlProfileRemaining_ = accessControlProfileCount;
+    remainingEntryCounts_ = entryCounts;
+    entryNameSpace_ = "";
+
+    signedDataAccessControlProfiles_ = cppbor::Array();
+    signedDataNamespaces_ = cppbor::Map();
+    signedDataCurrentNamespace_ = cppbor::Array();
+
+    _hidl_cb(support::resultOK());
+    return Void();
+}
+
+Return<void> WritableIdentityCredential::addAccessControlProfile(
+        uint16_t id, const hidl_vec<uint8_t>& readerCertificate, bool userAuthenticationRequired,
+        uint64_t timeoutMillis, uint64_t secureUserId, addAccessControlProfile_cb _hidl_cb) {
+    SecureAccessControlProfile profile;
+
+    if (numAccessControlProfileRemaining_ == 0) {
+        _hidl_cb(support::result(ResultCode::INVALID_DATA,
+                                 "numAccessControlProfileRemaining_ is 0 and expected non-zero"),
+                 profile);
+        return Void();
+    }
+
+    // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
+    // be zero.
+    if (!userAuthenticationRequired && timeoutMillis != 0) {
+        _hidl_cb(support::result(ResultCode::INVALID_DATA,
+                                 "userAuthenticationRequired is false but timeout is non-zero"),
+                 profile);
+        return Void();
+    }
+
+    profile.id = id;
+    profile.readerCertificate = readerCertificate;
+    profile.userAuthenticationRequired = userAuthenticationRequired;
+    profile.timeoutMillis = timeoutMillis;
+    profile.secureUserId = secureUserId;
+    optional<vector<uint8_t>> mac =
+            support::secureAccessControlProfileCalcMac(profile, storageKey_);
+    if (!mac) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error calculating MAC for profile"), profile);
+        return Void();
+    }
+    profile.mac = mac.value();
+
+    cppbor::Map profileMap;
+    profileMap.add("id", profile.id);
+    if (profile.readerCertificate.size() > 0) {
+        profileMap.add("readerCertificate", cppbor::Bstr(profile.readerCertificate));
+    }
+    if (profile.userAuthenticationRequired) {
+        profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
+        profileMap.add("timeoutMillis", profile.timeoutMillis);
+    }
+    signedDataAccessControlProfiles_.add(std::move(profileMap));
+
+    numAccessControlProfileRemaining_--;
+
+    _hidl_cb(support::resultOK(), profile);
+    return Void();
+}
+
+Return<void> WritableIdentityCredential::beginAddEntry(
+        const hidl_vec<uint16_t>& accessControlProfileIds, const hidl_string& nameSpace,
+        const hidl_string& name, uint32_t entrySize, beginAddEntry_cb _hidl_cb) {
+    if (numAccessControlProfileRemaining_ != 0) {
+        LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
+                   << " and expected zero";
+        _hidl_cb(support::result(ResultCode::INVALID_DATA,
+                                 "numAccessControlProfileRemaining_ is %zd and expected zero",
+                                 numAccessControlProfileRemaining_));
+        return Void();
+    }
+
+    if (remainingEntryCounts_.size() == 0) {
+        _hidl_cb(support::result(ResultCode::INVALID_DATA, "No more namespaces to add to"));
+        return Void();
+    }
+
+    // Handle initial beginEntry() call.
+    if (entryNameSpace_ == "") {
+        entryNameSpace_ = nameSpace;
+    }
+
+    // If the namespace changed...
+    if (nameSpace != entryNameSpace_) {
+        // Then check that all entries in the previous namespace have been added..
+        if (remainingEntryCounts_[0] != 0) {
+            _hidl_cb(support::result(ResultCode::INVALID_DATA,
+                                     "New namespace but %d entries remain to be added",
+                                     int(remainingEntryCounts_[0])));
+            return Void();
+        }
+        remainingEntryCounts_.erase(remainingEntryCounts_.begin());
+
+        if (signedDataCurrentNamespace_.size() > 0) {
+            signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
+            signedDataCurrentNamespace_ = cppbor::Array();
+        }
+    } else {
+        // Same namespace...
+        if (remainingEntryCounts_[0] == 0) {
+            _hidl_cb(support::result(ResultCode::INVALID_DATA,
+                                     "Same namespace but no entries remain to be added"));
+            return Void();
+        }
+        remainingEntryCounts_[0] -= 1;
+    }
+
+    entryAdditionalData_ =
+            support::entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
+
+    entryRemainingBytes_ = entrySize;
+    entryNameSpace_ = nameSpace;
+    entryName_ = name;
+    entryAccessControlProfileIds_ = accessControlProfileIds;
+    entryBytes_.resize(0);
+    // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
+
+    _hidl_cb(support::resultOK());
+    return Void();
+}
+
+Return<void> WritableIdentityCredential::addEntryValue(const hidl_vec<uint8_t>& content,
+                                                       addEntryValue_cb _hidl_cb) {
+    size_t contentSize = content.size();
+
+    if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
+        _hidl_cb(support::result(
+                         ResultCode::INVALID_DATA,
+                         "Passed in chunk of size %zd is bigger than kGcmChunkSize which is %zd",
+                         contentSize, IdentityCredentialStore::kGcmChunkSize),
+                 {});
+        return Void();
+    }
+    if (contentSize > entryRemainingBytes_) {
+        _hidl_cb(support::result(ResultCode::INVALID_DATA,
+                                 "Passed in chunk of size %zd is bigger than remaining space "
+                                 "of size %zd",
+                                 contentSize, entryRemainingBytes_),
+                 {});
+        return Void();
+    }
+
+    entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
+    entryRemainingBytes_ -= contentSize;
+    if (entryRemainingBytes_ > 0) {
+        if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
+            _hidl_cb(support::result(ResultCode::INVALID_DATA,
+                                     "Retrieved non-final chunk of size %zd but expected "
+                                     "kGcmChunkSize which is %zd",
+                                     contentSize, IdentityCredentialStore::kGcmChunkSize),
+                     {});
+            return Void();
+        }
+    }
+
+    optional<vector<uint8_t>> nonce = support::getRandom(12);
+    if (!nonce) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error getting nonce"), {});
+        return Void();
+    }
+    optional<vector<uint8_t>> encryptedContent =
+            support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
+    if (!encryptedContent) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error encrypting content"), {});
+        return Void();
+    }
+
+    if (entryRemainingBytes_ == 0) {
+        // TODO: ideally do do this without parsing the data (but still validate data is valid
+        // CBOR).
+        auto [item, _, message] = cppbor::parse(entryBytes_);
+        if (item == nullptr) {
+            _hidl_cb(support::result(ResultCode::INVALID_DATA, "Data is not valid CBOR"), {});
+            return Void();
+        }
+        cppbor::Map entryMap;
+        entryMap.add("name", entryName_);
+        entryMap.add("value", std::move(item));
+        cppbor::Array profileIdArray;
+        for (auto id : entryAccessControlProfileIds_) {
+            profileIdArray.add(id);
+        }
+        entryMap.add("accessControlProfiles", std::move(profileIdArray));
+        signedDataCurrentNamespace_.add(std::move(entryMap));
+    }
+
+    _hidl_cb(support::resultOK(), encryptedContent.value());
+    return Void();
+}
+
+Return<void> WritableIdentityCredential::finishAddingEntries(finishAddingEntries_cb _hidl_cb) {
+    if (signedDataCurrentNamespace_.size() > 0) {
+        signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
+    }
+    cppbor::Array popArray;
+    popArray.add("ProofOfProvisioning")
+            .add(docType_)
+            .add(std::move(signedDataAccessControlProfiles_))
+            .add(std::move(signedDataNamespaces_))
+            .add(testCredential_);
+    vector<uint8_t> encodedCbor = popArray.encode();
+
+    optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
+                                                                 encodedCbor,  // payload
+                                                                 {},           // additionalData
+                                                                 {});          // certificateChain
+    if (!signature) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error signing data"), {}, {});
+        return Void();
+    }
+
+    vector<uint8_t> credentialKeys;
+    if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error generating CredentialKeys"), {}, {});
+        return Void();
+    }
+
+    vector<uint8_t> credentialData;
+    if (!generateCredentialData(testCredential_ ? support::getTestHardwareBoundKey()
+                                                : support::getHardwareBoundKey(),
+                                docType_, testCredential_, credentialKeys, credentialData)) {
+        _hidl_cb(support::result(ResultCode::FAILED, "Error generating CredentialData"), {}, {});
+        return Void();
+    }
+
+    _hidl_cb(support::resultOK(), credentialData, signature.value());
+    return Void();
+}
+
+}  // namespace implementation
+}  // namespace identity
+}  // namespace hardware
+}  // namespace android
diff --git a/identity/1.0/default/WritableIdentityCredential.h b/identity/1.0/default/WritableIdentityCredential.h
new file mode 100644
index 0000000..9f4e303
--- /dev/null
+++ b/identity/1.0/default/WritableIdentityCredential.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
+#define ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
+
+#include <android/hardware/identity/1.0/IWritableIdentityCredential.h>
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <cppbor.h>
+
+namespace android {
+namespace hardware {
+namespace identity {
+namespace implementation {
+
+using ::std::string;
+using ::std::vector;
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::identity::V1_0::IWritableIdentityCredential;
+using ::android::hardware::identity::V1_0::Result;
+using ::android::hardware::identity::V1_0::ResultCode;
+using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
+
+class WritableIdentityCredential : public IWritableIdentityCredential {
+  public:
+    WritableIdentityCredential(const hidl_string& docType, bool testCredential)
+        : docType_(docType), testCredential_(testCredential) {}
+
+    // Creates the Credential Key. Returns false on failure. Must be called
+    // right after construction.
+    bool initialize();
+
+    // Methods from ::android::hardware::identity::IWritableIdentityCredential
+    // follow.
+    Return<void> getAttestationCertificate(const hidl_vec<uint8_t>& attestationChallenge,
+                                           getAttestationCertificate_cb _hidl_cb) override;
+
+    Return<void> startPersonalization(uint16_t accessControlProfileCount,
+                                      const hidl_vec<uint16_t>& entryCounts,
+                                      startPersonalization_cb _hidl_cb) override;
+
+    Return<void> addAccessControlProfile(uint16_t id, const hidl_vec<uint8_t>& readerCertificate,
+                                         bool userAuthenticationRequired, uint64_t timeoutMillis,
+                                         uint64_t secureUserId,
+                                         addAccessControlProfile_cb _hidl_cb) override;
+
+    Return<void> beginAddEntry(const hidl_vec<uint16_t>& accessControlProfileIds,
+                               const hidl_string& nameSpace, const hidl_string& name,
+                               uint32_t entrySize, beginAddEntry_cb _hidl_cb) override;
+
+    Return<void> addEntryValue(const hidl_vec<uint8_t>& content,
+                               addEntryValue_cb _hidl_cb) override;
+
+    Return<void> finishAddingEntries(finishAddingEntries_cb _hidl_cb) override;
+
+  private:
+    string docType_;
+    bool testCredential_;
+
+    // These are set in initialize().
+    vector<uint8_t> storageKey_;
+    vector<uint8_t> credentialPrivKey_;
+    vector<uint8_t> credentialPubKey_;
+
+    // These fields are initialized during startPersonalization()
+    size_t numAccessControlProfileRemaining_;
+    vector<uint16_t> remainingEntryCounts_;
+    cppbor::Array signedDataAccessControlProfiles_;
+    cppbor::Map signedDataNamespaces_;
+    cppbor::Array signedDataCurrentNamespace_;
+
+    // These fields are initialized during beginAddEntry()
+    size_t entryRemainingBytes_;
+    vector<uint8_t> entryAdditionalData_;
+    string entryNameSpace_;
+    string entryName_;
+    vector<uint16_t> entryAccessControlProfileIds_;
+    vector<uint8_t> entryBytes_;
+};
+
+}  // namespace implementation
+}  // namespace identity
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
diff --git a/identity/1.0/default/android.hardware.identity@1.0-service.example.rc b/identity/1.0/default/android.hardware.identity@1.0-service.example.rc
new file mode 100644
index 0000000..1eb7319
--- /dev/null
+++ b/identity/1.0/default/android.hardware.identity@1.0-service.example.rc
@@ -0,0 +1,3 @@
+service vendor.identity-1-0 /vendor/bin/hw/android.hardware.identity@1.0-service.example
+    class hal
+    user nobody
diff --git a/identity/1.0/default/service.cpp b/identity/1.0/default/service.cpp
new file mode 100644
index 0000000..839e803
--- /dev/null
+++ b/identity/1.0/default/service.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.identity@1.0-service"
+
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "IdentityCredentialStore.h"
+
+using android::hardware::joinRpcThreadpool;
+using android::hardware::identity::implementation::IdentityCredentialStore;
+
+int main(int /* argc */, char* argv[]) {
+    ::android::hardware::configureRpcThreadpool(1, true /*willJoinThreadpool*/);
+
+    ::android::base::InitLogging(argv, &android::base::StderrLogger);
+
+    auto identity_store = new IdentityCredentialStore();
+    auto status = identity_store->registerAsService();
+    if (status != android::OK) {
+        LOG(FATAL) << "Could not register service for IdentityCredentialStore 1.0 (" << status
+                   << ")";
+    }
+    joinRpcThreadpool();
+    return -1;  // Should never get here.
+}
diff --git a/identity/1.0/types.hal b/identity/1.0/types.hal
new file mode 100644
index 0000000..5aedfea
--- /dev/null
+++ b/identity/1.0/types.hal
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+package android.hardware.identity@1.0;
+
+/**
+ * The ResultCode enumeration is used to convey the status of an operation.
+ */
+enum ResultCode : int32_t {
+    /**
+     * Success.
+     */
+    OK = 0,
+
+    /**
+     * The operation failed. This is used as a generic catch-all for errors that don't belong
+     * in other categories, including memory/resource allocation failures and I/O errors.
+     */
+    FAILED = 1,
+
+    /**
+     * The passed data was invalid. This is a generic catch all for errors that don't belong
+     * in other categories related to parameter validation.
+     */
+    INVALID_DATA = 2,
+
+    /**
+     * The authToken parameter passed to IIdentityCredential.startRetrieval() is not valid.
+     */
+    INVALID_AUTH_TOKEN = 3,
+
+    /**
+     * The itemsRequest parameter passed to IIdentityCredential.startRetrieval() does not meet
+     * the requirements described in the documentation for that method.
+     */
+    INVALID_ITEMS_REQUEST_MESSAGE = 4,
+
+    /**
+     * The readerSignature parameter in IIdentityCredential.startRetrieval() is invalid,
+     * doesn't contain an embedded certificate chain, or the signature failed to
+     * validate.
+     */
+    READER_SIGNATURE_CHECK_FAILED = 5,
+
+    /**
+     * The sessionTranscript passed to startRetrieval() did not contain the ephmeral public
+     * key returned by createEphemeralPublicKey().
+     */
+    EPHEMERAL_PUBLIC_KEY_NOT_FOUND = 6,
+
+    /**
+     * An access condition related to user authentication was not satisfied.
+     */
+    USER_AUTHENTICATION_FAILED = 7,
+
+    /**
+     * An access condition related to reader authentication was not satisfied.
+     */
+    READER_AUTHENTICATION_FAILED = 8,
+
+    /**
+    * The request data element has no access control profiles associated so it cannot be accessed.
+    */
+    NO_ACCESS_CONTROL_PROFILES = 9,
+
+    /**
+     * The requested data element is not in the provided non-empty itemsRequest message.
+     */
+    NOT_IN_REQUEST_MESSAGE = 10,
+
+    /**
+     * The passed-in sessionTranscript doesn't match the previously passed-in sessionTranscript.
+     */
+    SESSION_TRANSCRIPT_MISMATCH = 11,
+};
+
+/**
+ * A result has a ResultCode and corresponding textual message.
+ */
+struct Result {
+    /**
+     * The result code.
+     *
+     * Implementations must not use values not defined in the ResultCode enumeration.
+     */
+    ResultCode code;
+
+    /**
+     * A human-readable message in English conveying more detail about a failure.
+     *
+     * If code is ResultCode::OK this field must be set to the empty string.
+     */
+    string message;
+};
+
+struct SecureAccessControlProfile {
+    /**
+     * id is a numeric identifier that must be unique within the context of a Credential and may be
+     * used to reference the profile.
+     */
+    uint16_t id;
+
+    /**
+     * readerCertificate, if non-empty, specifies a single X.509 certificate (not a chain
+     * of certificates) that must be used to authenticate requests. For details about how
+     * this is done, see the readerSignature paremter of IIdentityCredential.startRetrieval.
+     */
+    vec<uint8_t> readerCertificate;
+
+    /**
+     * if true, the user is required to authenticate to allow requests.  Required authentication
+     * fressness is specified by timeout below.
+     *
+     */
+    bool userAuthenticationRequired;
+
+    /**
+     * Timeout specifies the amount of time, in milliseconds, for which a user authentication (see
+     * above) is valid, if userAuthenticationRequired is set to true.  If userAuthenticationRequired
+     * is true and timout is zero then authentication is required for each reader session.
+     *
+     * If userAuthenticationRequired is false, timeout must be zero.
+     */
+    uint64_t timeoutMillis;
+
+    /**
+     * secureUserId must be non-zero if userAuthenticationRequired is true.
+     * It is not related to any Android user ID or UID, but is created in the
+     * Gatekeeper application in the secure environment.
+     */
+    uint64_t secureUserId;
+
+    /**
+     * The mac is used to authenticate the access control profile.  It contains:
+     *
+     *      AES-GCM-ENC(storageKey, R, {}, AccessControlProfile)
+     *
+     *  where AccessControlProfile is the CBOR map:
+     *
+     *      AccessControlProfile = {
+     *          "id": uint,
+     *          ? "readerCertificate" : bstr,
+     *          ? (
+     *              "userAuthenticationRequired" : bool,
+     *              "timeoutMillis" : uint,
+     *              "secureUserId" : uint
+     *          )
+     *      }
+     */
+    vec<uint8_t> mac;
+};
diff --git a/identity/1.0/vts/OWNERS b/identity/1.0/vts/OWNERS
new file mode 100644
index 0000000..6969910
--- /dev/null
+++ b/identity/1.0/vts/OWNERS
@@ -0,0 +1,2 @@
+swillden@google.com
+zeuthen@google.com
diff --git a/identity/1.0/vts/functional/Android.bp b/identity/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..03b49de
--- /dev/null
+++ b/identity/1.0/vts/functional/Android.bp
@@ -0,0 +1,36 @@
+//
+// 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.
+//
+
+cc_test {
+    name: "VtsHalIdentityCredentialTargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "VtsHalIdentityCredentialTargetTest.cpp",
+    ],
+    static_libs: [
+        "android.hardware.identity@1.0",
+        "android.hardware.identity-support-lib",
+        "android.hardware.keymaster@4.0",
+        "libcppbor",
+    ],
+    shared_libs: [
+        "libcrypto",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts-core",
+    ],
+}
diff --git a/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp b/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp
new file mode 100644
index 0000000..903e912
--- /dev/null
+++ b/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp
@@ -0,0 +1,527 @@
+/*
+ * 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 "IdentityCredentialHidlHalTest"
+
+#include <map>
+
+#include <android-base/logging.h>
+#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
+#include <android/hardware/identity/1.0/types.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+using std::map;
+using std::optional;
+using std::string;
+using std::vector;
+
+namespace android {
+namespace hardware {
+namespace identity {
+namespace test {
+
+using ::android::hardware::identity::V1_0::IIdentityCredential;
+using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
+using ::android::hardware::identity::V1_0::IWritableIdentityCredential;
+using ::android::hardware::identity::V1_0::Result;
+using ::android::hardware::identity::V1_0::ResultCode;
+using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
+using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+
+// ---------------------------------------------------------------------------
+// Test Data.
+// ---------------------------------------------------------------------------
+
+struct TestEntryData {
+    TestEntryData(string nameSpace, string name, vector<uint16_t> profileIds)
+        : nameSpace(nameSpace), name(name), profileIds(profileIds) {}
+
+    TestEntryData(string nameSpace, string name, const string& value, vector<uint16_t> profileIds)
+        : TestEntryData(nameSpace, name, profileIds) {
+        valueCbor = cppbor::Tstr(((const char*)value.data())).encode();
+    }
+    TestEntryData(string nameSpace, string name, const vector<uint8_t>& value,
+                  vector<uint16_t> profileIds)
+        : TestEntryData(nameSpace, name, profileIds) {
+        valueCbor = cppbor::Bstr(value).encode();
+    }
+    TestEntryData(string nameSpace, string name, bool value, vector<uint16_t> profileIds)
+        : TestEntryData(nameSpace, name, profileIds) {
+        valueCbor = cppbor::Bool(value).encode();
+    }
+    TestEntryData(string nameSpace, string name, int64_t value, vector<uint16_t> profileIds)
+        : TestEntryData(nameSpace, name, profileIds) {
+        if (value >= 0) {
+            valueCbor = cppbor::Uint(value).encode();
+        } else {
+            valueCbor = cppbor::Nint(-value).encode();
+        }
+    }
+
+    string nameSpace;
+    string name;
+    vector<uint8_t> valueCbor;
+    vector<uint16_t> profileIds;
+};
+
+struct TestProfile {
+    uint16_t id;
+    hidl_vec<uint8_t> readerCertificate;
+    bool userAuthenticationRequired;
+    uint64_t timeoutMillis;
+};
+
+/************************************
+ *   TEST DATA FOR AUTHENTICATION
+ ************************************/
+// Test authentication token for user authentication
+
+class IdentityCredentialStoreHidlTest : public ::testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        string serviceName = GetParam();
+        ASSERT_FALSE(serviceName.empty());
+        credentialStore_ = IIdentityCredentialStore::getService(serviceName);
+        ASSERT_NE(credentialStore_, nullptr);
+
+        credentialStore_->getHardwareInformation(
+                [&](const Result& result, const hidl_string& credentialStoreName,
+                    const hidl_string& credentialStoreAuthorName, uint32_t chunkSize,
+                    bool /* isDirectAccess */,
+                    const hidl_vec<hidl_string> /* supportedDocTypes */) {
+                    EXPECT_EQ("", result.message);
+                    ASSERT_EQ(ResultCode::OK, result.code);
+                    ASSERT_GT(credentialStoreName.size(), 0u);
+                    ASSERT_GT(credentialStoreAuthorName.size(), 0u);
+                    ASSERT_GE(chunkSize, 256u);  // Chunk sizes < APDU buffer won't be supported
+                    dataChunkSize_ = chunkSize;
+                });
+    }
+    virtual void TearDown() override {}
+
+    uint32_t dataChunkSize_ = 0;
+
+    sp<IIdentityCredentialStore> credentialStore_;
+};
+
+TEST_P(IdentityCredentialStoreHidlTest, HardwareConfiguration) {
+    credentialStore_->getHardwareInformation(
+            [&](const Result& result, const hidl_string& credentialStoreName,
+                const hidl_string& credentialStoreAuthorName, uint32_t chunkSize,
+                bool /* isDirectAccess */, const hidl_vec<hidl_string> /* supportedDocTypes */) {
+                EXPECT_EQ("", result.message);
+                ASSERT_EQ(ResultCode::OK, result.code);
+                ASSERT_GT(credentialStoreName.size(), 0u);
+                ASSERT_GT(credentialStoreAuthorName.size(), 0u);
+                ASSERT_GE(chunkSize, 256u);  // Chunk sizes < APDU buffer won't be supported
+            });
+}
+
+TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) {
+    // First, generate a key-pair for the reader since its public key will be
+    // part of the request data.
+    optional<vector<uint8_t>> readerKeyPKCS8 = support::createEcKeyPair();
+    ASSERT_TRUE(readerKeyPKCS8);
+    optional<vector<uint8_t>> readerPublicKey =
+            support::ecKeyPairGetPublicKey(readerKeyPKCS8.value());
+    optional<vector<uint8_t>> readerKey = support::ecKeyPairGetPrivateKey(readerKeyPKCS8.value());
+    string serialDecimal = "1234";
+    string issuer = "Android Open Source Project";
+    string subject = "Android IdentityCredential VTS Test";
+    time_t validityNotBefore = time(nullptr);
+    time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
+    optional<vector<uint8_t>> readerCertificate = support::ecPublicKeyGenerateCertificate(
+            readerPublicKey.value(), readerKey.value(), serialDecimal, issuer, subject,
+            validityNotBefore, validityNotAfter);
+    ASSERT_TRUE(readerCertificate);
+
+    // Make the portrait image really big (just shy of 256 KiB) to ensure that
+    // the chunking code gets exercised.
+    vector<uint8_t> portraitImage;
+    portraitImage.resize(256 * 1024 - 10);
+    for (size_t n = 0; n < portraitImage.size(); n++) {
+        portraitImage[n] = (uint8_t)n;
+    }
+
+    // Access control profiles:
+    const vector<TestProfile> testProfiles = {// Profile 0 (reader authentication)
+                                              {0, readerCertificate.value(), false, 0},
+                                              // Profile 1 (no authentication)
+                                              {1, {}, false, 0}};
+
+    HardwareAuthToken authToken = {};
+
+    // Here's the actual test data:
+    const vector<TestEntryData> testEntries = {
+            {"PersonalData", "Last name", string("Turing"), vector<uint16_t>{0, 1}},
+            {"PersonalData", "Birth date", string("19120623"), vector<uint16_t>{0, 1}},
+            {"PersonalData", "First name", string("Alan"), vector<uint16_t>{0, 1}},
+            {"PersonalData", "Home address", string("Maida Vale, London, England"),
+             vector<uint16_t>{0}},
+            {"Image", "Portrait image", portraitImage, vector<uint16_t>{0, 1}},
+    };
+    const vector<uint16_t> testEntriesEntryCounts = {static_cast<uint16_t>(testEntries.size() - 1),
+                                                     1u};
+
+    string cborPretty;
+    sp<IWritableIdentityCredential> writableCredential;
+
+    hidl_vec<uint8_t> empty{0};
+
+    string docType = "org.iso.18013-5.2019.mdl";
+    bool testCredential = true;
+    Result result;
+    credentialStore_->createCredential(
+            docType, testCredential,
+            [&](const Result& _result, const sp<IWritableIdentityCredential>& _writableCredential) {
+                result = _result;
+                writableCredential = _writableCredential;
+            });
+    EXPECT_EQ("", result.message);
+    ASSERT_EQ(ResultCode::OK, result.code);
+    ASSERT_NE(writableCredential, nullptr);
+
+    string challenge = "attestationChallenge";
+    vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
+    vector<uint8_t> attestationCertificate;
+    writableCredential->getAttestationCertificate(
+            attestationChallenge,
+            [&](const Result& _result, const hidl_vec<uint8_t>& _attestationCertificate) {
+                result = _result;
+                attestationCertificate = _attestationCertificate;
+            });
+    EXPECT_EQ("", result.message);
+    ASSERT_EQ(ResultCode::OK, result.code);
+
+    writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts,
+                                             [&](const Result& _result) { result = _result; });
+    EXPECT_EQ("", result.message);
+    ASSERT_EQ(ResultCode::OK, result.code);
+
+    vector<SecureAccessControlProfile> returnedSecureProfiles;
+    for (const auto& testProfile : testProfiles) {
+        SecureAccessControlProfile profile;
+        writableCredential->addAccessControlProfile(
+                testProfile.id, testProfile.readerCertificate,
+                testProfile.userAuthenticationRequired, testProfile.timeoutMillis,
+                0,  // secureUserId
+                [&](const Result& _result, const SecureAccessControlProfile& _profile) {
+                    result = _result;
+                    profile = _profile;
+                });
+        EXPECT_EQ("", result.message);
+        ASSERT_EQ(ResultCode::OK, result.code);
+        ASSERT_EQ(testProfile.id, profile.id);
+        ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate);
+        ASSERT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired);
+        ASSERT_EQ(testProfile.timeoutMillis, profile.timeoutMillis);
+        ASSERT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size());
+        returnedSecureProfiles.push_back(profile);
+    }
+
+    // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
+    // is a little hacky but it works well enough.
+    map<const TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
+
+    for (const auto& entry : testEntries) {
+        vector<vector<uint8_t>> chunks = support::chunkVector(entry.valueCbor, dataChunkSize_);
+
+        writableCredential->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name,
+                                          entry.valueCbor.size(),
+                                          [&](const Result& _result) { result = _result; });
+        EXPECT_EQ("", result.message);
+        ASSERT_EQ(ResultCode::OK, result.code);
+
+        vector<vector<uint8_t>> encryptedChunks;
+        for (const auto& chunk : chunks) {
+            writableCredential->addEntryValue(
+                    chunk, [&](const Result& result, hidl_vec<uint8_t> encryptedContent) {
+                        EXPECT_EQ("", result.message);
+                        ASSERT_EQ(ResultCode::OK, result.code);
+                        ASSERT_GT(encryptedContent.size(), 0u);
+                        encryptedChunks.push_back(encryptedContent);
+                    });
+        }
+        encryptedBlobs[&entry] = encryptedChunks;
+    }
+
+    vector<uint8_t> credentialData;
+    vector<uint8_t> proofOfProvisioningSignature;
+    writableCredential->finishAddingEntries(
+            [&](const Result& _result, const hidl_vec<uint8_t>& _credentialData,
+                const hidl_vec<uint8_t>& _proofOfProvisioningSignature) {
+                result = _result;
+                credentialData = _credentialData;
+                proofOfProvisioningSignature = _proofOfProvisioningSignature;
+            });
+    EXPECT_EQ("", result.message);
+    ASSERT_EQ(ResultCode::OK, result.code);
+
+    optional<vector<uint8_t>> proofOfProvisioning =
+            support::coseSignGetPayload(proofOfProvisioningSignature);
+    ASSERT_TRUE(proofOfProvisioning);
+    cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
+    EXPECT_EQ(
+            "[\n"
+            "  'ProofOfProvisioning',\n"
+            "  'org.iso.18013-5.2019.mdl',\n"
+            "  [\n"
+            "    {\n"
+            "      'id' : 0,\n"
+            "      'readerCertificate' : <not printed>,\n"
+            "    },\n"
+            "    {\n"
+            "      'id' : 1,\n"
+            "    },\n"
+            "  ],\n"
+            "  {\n"
+            "    'PersonalData' : [\n"
+            "      {\n"
+            "        'name' : 'Last name',\n"
+            "        'value' : 'Turing',\n"
+            "        'accessControlProfiles' : [0, 1, ],\n"
+            "      },\n"
+            "      {\n"
+            "        'name' : 'Birth date',\n"
+            "        'value' : '19120623',\n"
+            "        'accessControlProfiles' : [0, 1, ],\n"
+            "      },\n"
+            "      {\n"
+            "        'name' : 'First name',\n"
+            "        'value' : 'Alan',\n"
+            "        'accessControlProfiles' : [0, 1, ],\n"
+            "      },\n"
+            "      {\n"
+            "        'name' : 'Home address',\n"
+            "        'value' : 'Maida Vale, London, England',\n"
+            "        'accessControlProfiles' : [0, ],\n"
+            "      },\n"
+            "    ],\n"
+            "    'Image' : [\n"
+            "      {\n"
+            "        'name' : 'Portrait image',\n"
+            "        'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
+            "        'accessControlProfiles' : [0, 1, ],\n"
+            "      },\n"
+            "    ],\n"
+            "  },\n"
+            "  true,\n"
+            "]",
+            cborPretty);
+
+    optional<vector<uint8_t>> credentialPubKey =
+            support::certificateChainGetTopMostKey(attestationCertificate);
+    ASSERT_TRUE(credentialPubKey);
+    EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+                                                 {},  // Additional data
+                                                 credentialPubKey.value()));
+    writableCredential = nullptr;
+
+    // Now that the credential has been provisioned, read it back and check the
+    // correct data is returned.
+    sp<IIdentityCredential> credential;
+    credentialStore_->getCredential(
+            credentialData, [&](const Result& _result, const sp<IIdentityCredential>& _credential) {
+                result = _result;
+                credential = _credential;
+            });
+    EXPECT_EQ("", result.message);
+    ASSERT_EQ(ResultCode::OK, result.code);
+    ASSERT_NE(credential, nullptr);
+
+    optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
+    ASSERT_TRUE(readerEphemeralKeyPair);
+    optional<vector<uint8_t>> readerEphemeralPublicKey =
+            support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
+    credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value(),
+                                            [&](const Result& _result) { result = _result; });
+    EXPECT_EQ("", result.message);
+    ASSERT_EQ(ResultCode::OK, result.code);
+
+    vector<uint8_t> ephemeralKeyPair;
+    credential->createEphemeralKeyPair(
+            [&](const Result& _result, const hidl_vec<uint8_t>& _ephemeralKeyPair) {
+                result = _result;
+                ephemeralKeyPair = _ephemeralKeyPair;
+            });
+    EXPECT_EQ("", result.message);
+    ASSERT_EQ(ResultCode::OK, result.code);
+    optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
+
+    // Calculate requestData field and sign it with the reader key.
+    auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ephemeralPublicKey.value());
+    ASSERT_TRUE(getXYSuccess);
+    cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
+    vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
+    vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
+    cppbor::Array sessionTranscript = cppbor::Array()
+                                              .add(cppbor::Semantic(24, deviceEngagementBytes))
+                                              .add(cppbor::Semantic(24, eReaderPubBytes));
+    vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
+
+    vector<uint8_t> itemsRequestBytes =
+            cppbor::Map("nameSpaces",
+                        cppbor::Map()
+                                .add("PersonalData", cppbor::Map()
+                                                             .add("Last name", false)
+                                                             .add("Birth date", false)
+                                                             .add("First name", false)
+                                                             .add("Home address", true))
+                                .add("Image", cppbor::Map().add("Portrait image", false)))
+                    .encode();
+    cborPretty = support::cborPrettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
+    EXPECT_EQ(
+            "{\n"
+            "  'nameSpaces' : {\n"
+            "    'PersonalData' : {\n"
+            "      'Last name' : false,\n"
+            "      'Birth date' : false,\n"
+            "      'First name' : false,\n"
+            "      'Home address' : true,\n"
+            "    },\n"
+            "    'Image' : {\n"
+            "      'Portrait image' : false,\n"
+            "    },\n"
+            "  },\n"
+            "}",
+            cborPretty);
+    vector<uint8_t> dataToSign = cppbor::Array()
+                                         .add("ReaderAuthentication")
+                                         .add(sessionTranscript.clone())
+                                         .add(cppbor::Semantic(24, itemsRequestBytes))
+                                         .encode();
+    optional<vector<uint8_t>> readerSignature =
+            support::coseSignEcDsa(readerKey.value(), {},  // content
+                                   dataToSign,             // detached content
+                                   readerCertificate.value());
+    ASSERT_TRUE(readerSignature);
+
+    credential->startRetrieval(returnedSecureProfiles, authToken, itemsRequestBytes,
+                               sessionTranscriptBytes, readerSignature.value(),
+                               testEntriesEntryCounts,
+                               [&](const Result& _result) { result = _result; });
+    EXPECT_EQ("", result.message);
+    ASSERT_EQ(ResultCode::OK, result.code);
+
+    for (const auto& entry : testEntries) {
+        credential->startRetrieveEntryValue(entry.nameSpace, entry.name, entry.valueCbor.size(),
+                                            entry.profileIds,
+                                            [&](const Result& _result) { result = _result; });
+        EXPECT_EQ("", result.message);
+        ASSERT_EQ(ResultCode::OK, result.code);
+
+        auto it = encryptedBlobs.find(&entry);
+        ASSERT_NE(it, encryptedBlobs.end());
+        const vector<vector<uint8_t>>& encryptedChunks = it->second;
+
+        vector<uint8_t> content;
+        for (const auto& encryptedChunk : encryptedChunks) {
+            vector<uint8_t> chunk;
+            credential->retrieveEntryValue(
+                    encryptedChunk, [&](const Result& _result, const hidl_vec<uint8_t>& _chunk) {
+                        result = _result;
+                        chunk = _chunk;
+                    });
+            EXPECT_EQ("", result.message);
+            ASSERT_EQ(ResultCode::OK, result.code);
+            content.insert(content.end(), chunk.begin(), chunk.end());
+        }
+        EXPECT_EQ(content, entry.valueCbor);
+    }
+
+    // Generate the key that will be used to sign AuthenticatedData.
+    vector<uint8_t> signingKeyBlob;
+    vector<uint8_t> signingKeyCertificate;
+    credential->generateSigningKeyPair([&](const Result& _result,
+                                           const hidl_vec<uint8_t> _signingKeyBlob,
+                                           const hidl_vec<uint8_t> _signingKeyCertificate) {
+        result = _result;
+        signingKeyBlob = _signingKeyBlob;
+        signingKeyCertificate = _signingKeyCertificate;
+    });
+    EXPECT_EQ("", result.message);
+    ASSERT_EQ(ResultCode::OK, result.code);
+
+    vector<uint8_t> mac;
+    vector<uint8_t> deviceNameSpacesBytes;
+    credential->finishRetrieval(signingKeyBlob,
+                                [&](const Result& _result, const hidl_vec<uint8_t> _mac,
+                                    const hidl_vec<uint8_t> _deviceNameSpacesBytes) {
+                                    result = _result;
+                                    mac = _mac;
+                                    deviceNameSpacesBytes = _deviceNameSpacesBytes;
+                                });
+    EXPECT_EQ("", result.message);
+    ASSERT_EQ(ResultCode::OK, result.code);
+    cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
+    ASSERT_EQ(
+            "{\n"
+            "  'PersonalData' : {\n"
+            "    'Last name' : 'Turing',\n"
+            "    'Birth date' : '19120623',\n"
+            "    'First name' : 'Alan',\n"
+            "    'Home address' : 'Maida Vale, London, England',\n"
+            "  },\n"
+            "  'Image' : {\n"
+            "    'Portrait image' : <bstr size=262134 "
+            "sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
+            "  },\n"
+            "}",
+            cborPretty);
+    // The data that is MACed is ["DeviceAuthentication", sessionTranscriptBytes, docType,
+    // deviceNameSpacesBytes] so build up that structure
+    cppbor::Array deviceAuthentication;
+    deviceAuthentication.add("DeviceAuthentication");
+    deviceAuthentication.add(sessionTranscript.clone());
+    deviceAuthentication.add(docType);
+    deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
+    vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode();
+    optional<vector<uint8_t>> signingPublicKey =
+            support::certificateChainGetTopMostKey(signingKeyCertificate);
+    EXPECT_TRUE(signingPublicKey);
+
+    // Derive the key used for MACing.
+    optional<vector<uint8_t>> readerEphemeralPrivateKey =
+            support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
+    optional<vector<uint8_t>> sharedSecret =
+            support::ecdh(signingPublicKey.value(), readerEphemeralPrivateKey.value());
+    ASSERT_TRUE(sharedSecret);
+    vector<uint8_t> salt = {0x00};
+    vector<uint8_t> info = {};
+    optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
+    ASSERT_TRUE(derivedKey);
+    optional<vector<uint8_t>> calculatedMac =
+            support::coseMac0(derivedKey.value(), {},        // payload
+                              encodedDeviceAuthentication);  // detached content
+    ASSERT_TRUE(calculatedMac);
+    EXPECT_EQ(mac, calculatedMac);
+}
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, IdentityCredentialStoreHidlTest,
+                         testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IIdentityCredentialStore::descriptor)),
+                         android::hardware::PrintInstanceNameToString);
+
+}  // namespace test
+}  // namespace identity
+}  // namespace hardware
+}  // namespace android
diff --git a/identity/support/Android.bp b/identity/support/Android.bp
new file mode 100644
index 0000000..38dc10b
--- /dev/null
+++ b/identity/support/Android.bp
@@ -0,0 +1,103 @@
+// 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.
+//
+
+cc_library {
+    name: "android.hardware.identity-support-lib",
+    vendor_available: true,
+    srcs: [
+        "src/IdentityCredentialSupport.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    shared_libs: [
+        "android.hardware.identity@1.0",
+        "libcrypto",
+        "libbase",
+        "libhidlbase",
+        "libhardware",
+    ],
+    static_libs: [
+        "libcppbor",
+    ],
+}
+
+cc_test {
+    name: "android.hardware.identity-support-lib-test",
+    srcs: [
+        "tests/IdentityCredentialSupportTest.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.identity-support-lib",
+        "android.hardware.identity@1.0",
+        "libcrypto",
+        "libbase",
+        "libhidlbase",
+        "libhardware",
+    ],
+    static_libs: [
+        "libcppbor",
+        "libgmock",
+    ],
+    test_suites: ["general-tests"],
+}
+
+// --
+
+cc_library {
+    name: "libcppbor",
+    vendor_available: true,
+    host_supported: true,
+    srcs: [
+        "src/cppbor.cpp",
+        "src/cppbor_parse.cpp",
+    ],
+    export_include_dirs: [
+        "include/cppbor",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+}
+
+cc_test {
+    name: "cppbor_test",
+    srcs: [
+        "tests/cppbor_test.cpp",
+    ],
+    shared_libs: [
+        "libcppbor",
+        "libbase",
+    ],
+    static_libs: [
+        "libgmock",
+    ],
+    test_suites: ["general-tests"],
+}
+
+cc_test_host {
+    name: "cppbor_host_test",
+    srcs: [
+        "tests/cppbor_test.cpp",
+    ],
+    shared_libs: [
+        "libcppbor",
+        "libbase",
+    ],
+    static_libs: [
+        "libgmock",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
new file mode 100644
index 0000000..485571a
--- /dev/null
+++ b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_
+#define IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_
+
+#include <cstdint>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <android/hardware/identity/1.0/types.h>
+
+namespace android {
+namespace hardware {
+namespace identity {
+namespace support {
+
+using ::std::optional;
+using ::std::string;
+using ::std::tuple;
+using ::std::vector;
+
+using ::android::hardware::identity::V1_0::Result;
+using ::android::hardware::identity::V1_0::ResultCode;
+using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
+
+// ---------------------------------------------------------------------------
+// Miscellaneous utilities.
+// ---------------------------------------------------------------------------
+
+// Dumps the data in |data| to stderr. The written data will be of the following
+// form for the call hexdump("signature", data) where |data| is of size 71:
+//
+//   signature: dumping 71 bytes
+//   0000  30 45 02 21 00 ac c6 12 60 56 a2 e9 ee 16 be 14  0E.!....`V......
+//   0010  69 7f c4 00 95 8c e8 55 1f 22 de 34 0b 08 8a 3b  i......U.".4...;
+//   0020  a0 56 54 05 07 02 20 58 77 d9 8c f9 eb 41 df fd  .VT... Xw....A..
+//   0030  c1 a3 14 e0 bf b0 a2 c5 0c b6 85 8c 4a 0d f9 2b  ............J..+
+//   0040  b7 8f d2 1d 9b 11 ac                             .......
+//
+// This should only be used for debugging.
+void hexdump(const string& name, const vector<uint8_t>& data);
+
+string encodeHex(const string& str);
+
+string encodeHex(const vector<uint8_t>& data);
+
+string encodeHex(const uint8_t* data, size_t dataLen);
+
+optional<vector<uint8_t>> decodeHex(const string& hexEncoded);
+
+// ---------------------------------------------------------------------------
+// CBOR utilities.
+// ---------------------------------------------------------------------------
+
+// Returns pretty-printed CBOR for |value|.
+//
+// Only valid CBOR should be passed to this function.
+//
+// If a byte-string is larger than |maxBStrSize| its contents will not be
+// printed, instead the value of the form "<bstr size=1099016
+// sha1=ef549cca331f73dfae2090e6a37c04c23f84b07b>" will be printed. Pass zero
+// for |maxBStrSize| to disable this.
+//
+// The |mapKeysToNotPrint| parameter specifies the name of map values
+// to not print. This is useful for unit tests.
+string cborPrettyPrint(const vector<uint8_t>& encodedCbor, size_t maxBStrSize = 32,
+                       const vector<string>& mapKeysToNotPrint = {});
+
+// ---------------------------------------------------------------------------
+// Crypto functionality / abstraction.
+// ---------------------------------------------------------------------------
+
+constexpr size_t kAesGcmIvSize = 12;
+constexpr size_t kAesGcmTagSize = 16;
+constexpr size_t kAes128GcmKeySize = 16;
+
+// Returns |numBytes| bytes of random data.
+optional<vector<uint8_t>> getRandom(size_t numBytes);
+
+// Calculates the SHA-256 of |data|.
+vector<uint8_t> sha256(const vector<uint8_t>& data);
+
+// Decrypts |encryptedData| using |key| and |additionalAuthenticatedData|,
+// returns resulting plaintext. The format of |encryptedData| must
+// be as specified in the encryptAes128Gcm() function.
+optional<vector<uint8_t>> decryptAes128Gcm(const vector<uint8_t>& key,
+                                           const vector<uint8_t>& encryptedData,
+                                           const vector<uint8_t>& additionalAuthenticatedData);
+
+// Encrypts |data| with |key| and |additionalAuthenticatedData| using |nonce|,
+// returns the resulting (nonce || ciphertext || tag).
+optional<vector<uint8_t>> encryptAes128Gcm(const vector<uint8_t>& key, const vector<uint8_t>& nonce,
+                                           const vector<uint8_t>& data,
+                                           const vector<uint8_t>& additionalAuthenticatedData);
+
+// ---------------------------------------------------------------------------
+// EC crypto functionality / abstraction (only supports P-256).
+// ---------------------------------------------------------------------------
+
+// Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the
+// PKCS#8 encoded key-pair.
+//
+optional<vector<uint8_t>> createEcKeyPair();
+
+// For an EC key |keyPair| encoded in PKCS#8 format, extracts the public key in
+// uncompressed point form.
+//
+optional<vector<uint8_t>> ecKeyPairGetPublicKey(const vector<uint8_t>& keyPair);
+
+// For an EC key |keyPair| encoded in PKCS#8 format, extracts the private key as
+// an EC uncompressed key.
+//
+optional<vector<uint8_t>> ecKeyPairGetPrivateKey(const vector<uint8_t>& keyPair);
+
+// For an EC key |keyPair| encoded in PKCS#8 format, creates a PKCS#12 structure
+// with the key-pair (not using a password to encrypt the data). The public key
+// in the created structure is included as a certificate, using the given fields
+// |serialDecimal|, |issuer|, |subject|, |validityNotBefore|, and
+// |validityNotAfter|.
+//
+optional<vector<uint8_t>> ecKeyPairGetPkcs12(const vector<uint8_t>& keyPair, const string& name,
+                                             const string& serialDecimal, const string& issuer,
+                                             const string& subject, time_t validityNotBefore,
+                                             time_t validityNotAfter);
+
+// Signs |data| with |key| (which must be in the format returned by
+// ecKeyPairGetPrivateKey()). Signature is returned and will be in DER format.
+//
+optional<vector<uint8_t>> signEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data);
+
+// Calculates the HMAC with SHA-256 for |data| using |key|. The calculated HMAC
+// is returned and will be 32 bytes.
+//
+optional<vector<uint8_t>> hmacSha256(const vector<uint8_t>& key, const vector<uint8_t>& data);
+
+// Checks that |signature| (in DER format) is a valid signature of |digest|,
+// made with |publicKey| (which must be in the format returned by
+// ecKeyPairGetPublicKey()).
+//
+bool checkEcDsaSignature(const vector<uint8_t>& digest, const vector<uint8_t>& signature,
+                         const vector<uint8_t>& publicKey);
+
+// Extracts the public-key from the top-most certificate in |certificateChain|
+// (which should be a concatenated chain of DER-encoded X.509 certificates).
+//
+// The returned public key will be in the same format as returned by
+// ecKeyPairGetPublicKey().
+//
+optional<vector<uint8_t>> certificateChainGetTopMostKey(const vector<uint8_t>& certificateChain);
+
+// Generates a X.509 certificate for |publicKey| (which must be in the format
+// returned by ecKeyPairGetPublicKey()).
+//
+// The certificate is signed by |signingKey| (which must be in the format
+// returned by ecKeyPairGetPrivateKey())
+//
+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);
+
+// Performs Elliptic-curve Diffie-Helman using |publicKey| (which must be in the
+// format returned by ecKeyPairGetPublicKey()) and |privateKey| (which must be
+// in the format returned by ecKeyPairGetPrivateKey()).
+//
+// On success, the computed shared secret is returned.
+//
+optional<vector<uint8_t>> ecdh(const vector<uint8_t>& publicKey, const vector<uint8_t>& privateKey);
+
+// Key derivation function using SHA-256, conforming to RFC 5869.
+//
+// On success, the derived key is returned.
+//
+optional<vector<uint8_t>> hkdf(const vector<uint8_t>& sharedSecret, const vector<uint8_t>& salt,
+                               const vector<uint8_t>& info, size_t size);
+
+// Returns the X and Y coordinates from |publicKey| (which must be in the format
+// returned by ecKeyPairGetPublicKey()).
+//
+// Success is indicated by the first value in the returned tuple. If successful,
+// the returned coordinates will be in uncompressed form.
+//
+tuple<bool, vector<uint8_t>, vector<uint8_t>> ecPublicKeyGetXandY(const vector<uint8_t>& publicKey);
+
+// Concatenates all certificates into |certificateChain| together into a
+// single bytestring.
+//
+// This is the reverse operation of certificateChainSplit().
+vector<uint8_t> certificateChainJoin(const vector<vector<uint8_t>>& certificateChain);
+
+// Splits all the certificates in a single bytestring into individual
+// certificates.
+//
+// Returns nothing if |certificateChain| contains invalid data.
+//
+// This is the reverse operation of certificateChainJoin().
+optional<vector<vector<uint8_t>>> certificateChainSplit(const vector<uint8_t>& certificateChain);
+
+// Validates that the certificate chain is valid. In particular, checks that each
+// certificate in the chain is signed by the public key in the following certificate.
+//
+// Returns false if |certificateChain| failed validation or if each certificate
+// is not signed by its successor.
+//
+bool certificateChainValidate(const vector<uint8_t>& certificateChain);
+
+// Signs |data| and |detachedContent| with |key| (which must be in the format
+// returned by ecKeyPairGetPrivateKey()).
+//
+// On success, the Signature is returned and will be in COSE_Sign1 format.
+//
+// If |certificateChain| is non-empty it's included in the 'x5chain'
+// protected header element (as as described in'draft-ietf-cose-x509-04').
+//
+optional<vector<uint8_t>> coseSignEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data,
+                                        const vector<uint8_t>& detachedContent,
+                                        const vector<uint8_t>& certificateChain);
+
+// Checks that |signatureCoseSign1| (in COSE_Sign1 format) is a valid signature
+// made with |public_key| (which must be in the format returned by
+// ecKeyPairGetPublicKey()) where |detachedContent| is the detached content.
+//
+bool coseCheckEcDsaSignature(const vector<uint8_t>& signatureCoseSign1,
+                             const vector<uint8_t>& detachedContent,
+                             const vector<uint8_t>& publicKey);
+
+// Extracts the payload from a COSE_Sign1.
+optional<vector<uint8_t>> coseSignGetPayload(const vector<uint8_t>& signatureCoseSign1);
+
+// Extracts the X.509 certificate chain, if present. Returns the data as a
+// concatenated chain of DER-encoded X.509 certificates
+//
+// Returns nothing if there is no 'x5chain' element or an error occurs.
+//
+optional<vector<uint8_t>> coseSignGetX5Chain(const vector<uint8_t>& signatureCoseSign1);
+
+// MACs |data| and |detachedContent| with |key| (which can be any sequence of
+// bytes).
+//
+// If successful, the MAC is returned and will be in COSE_Mac0 format.
+//
+optional<vector<uint8_t>> coseMac0(const vector<uint8_t>& key, const vector<uint8_t>& data,
+                                   const vector<uint8_t>& detachedContent);
+
+// ---------------------------------------------------------------------------
+// Platform abstraction.
+// ---------------------------------------------------------------------------
+
+// Returns the hardware-bound AES-128 key.
+const vector<uint8_t>& getHardwareBoundKey();
+
+// ---------------------------------------------------------------------------
+// Utility functions specific to IdentityCredential.
+// ---------------------------------------------------------------------------
+
+// Returns a reference to a Result with code OK and empty message.
+const Result& resultOK();
+
+// Returns a new Result with the given code and message.
+Result result(ResultCode code, const char* format, ...) __attribute__((format(printf, 2, 3)));
+
+// Splits the given bytestring into chunks. If the given vector is smaller or equal to
+// |maxChunkSize| a vector with |content| as the only element is returned. Otherwise
+// |content| is split into N vectors each of size |maxChunkSize| except the final element
+// may be smaller than |maxChunkSize|.
+vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxChunkSize);
+
+// Calculates the MAC for |profile| using |storageKey|.
+optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
+        const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey);
+
+// Checks authenticity of the MAC in |profile| using |storageKey|.
+bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
+                                        const vector<uint8_t>& storageKey);
+
+// Returns the testing AES-128 key where all bits are set to 0.
+const vector<uint8_t>& getTestHardwareBoundKey();
+
+// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method.
+vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
+                                          const vector<uint16_t> accessControlProfileIds);
+
+}  // namespace support
+}  // namespace identity
+}  // namespace hardware
+}  // namespace android
+
+#endif  // IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_
diff --git a/identity/support/include/cppbor/README.md b/identity/support/include/cppbor/README.md
new file mode 100644
index 0000000..723bfcf
--- /dev/null
+++ b/identity/support/include/cppbor/README.md
@@ -0,0 +1,216 @@
+CppBor: A Modern C++ CBOR Parser and Generator
+==============================================
+
+CppBor provides a natural and easy-to-use syntax for constructing and
+parsing CBOR messages.  It does not (yet) support all features of
+CBOR, nor (yet) support validation against CDDL schemata, though both
+are planned.  CBOR features that aren't supported include:
+
+* Indefinite length values
+* Semantic tagging
+* Floating point
+
+CppBor requires C++-17.
+
+## CBOR representation
+
+CppBor represents CBOR data items as instances of the `Item` class or,
+more precisely, as instances of subclasses of `Item`, since `Item` is a
+pure interface.  The subclasses of `Item` correspond almost one-to-one
+with CBOR major types, and are named to match the CDDL names to which
+they correspond.  They are:
+
+* `Uint` corresponds to major type 0, and can hold unsigned integers
+  up through (2^64 - 1).
+* `Nint` corresponds to major type 1.  It can only hold values from -1
+  to -(2^63 - 1), since it's internal representation is an int64_t.
+  This can be fixed, but it seems unlikely that applications will need
+  the omitted range from -(2^63) to (2^64 - 1), since it's
+  inconvenient to represent them in many programming languages.
+* `Int` is an abstract base of `Uint` and `Nint` that facilitates
+  working with all signed integers representable with int64_t.
+* `Bstr` corresponds to major type 2, a byte string.
+* `Tstr` corresponds to major type 3, a text string.
+* `Array` corresponds to major type 4, an Array.  It holds a
+  variable-length array of `Item`s.
+* `Map` corresponds to major type 5, a Map.  It holds a
+  variable-length array of pairs of `Item`s.
+* `Simple` corresponds to major type 7.  It's an abstract class since
+  items require more specific type.
+* `Bool` is the only currently-implemented subclass of `Simple`.
+
+Note that major type 6, semantic tag, is not yet implemented.
+
+In practice, users of CppBor will rarely use most of these classes
+when generating CBOR encodings.  This is because CppBor provides
+straightforward conversions from the obvious normal C++ types.
+Specifically, the following conversions are provided in appropriate
+contexts:
+
+* Signed and unsigned integers convert to `Uint` or `Nint`, as
+  appropriate.
+* `std::string`, `std::string_view`, `const char*` and
+  `std::pair<char iterator, char iterator>` convert to `Tstr`.
+* `std::vector<uint8_t>`, `std::pair<uint8_t iterator, uint8_t
+  iterator>` and `std::pair<uint8_t*, size_t>` convert to `Bstr`.
+* `bool` converts to `Bool`.
+
+## CBOR generation
+
+### Complete tree generation
+
+The set of `encode` methods in `Item` provide the interface for
+producing encoded CBOR.  The basic process for "complete tree"
+generation (as opposed to "incremental" generation, which is discussed
+below) is to construct an `Item` which models the data to be encoded,
+and then call one of the `encode` methods, whichever is convenient for
+the encoding destination.  A trivial example:
+
+```
+cppbor::Uint val(0);
+std::vector<uint8_t> encoding = val.encode();
+```
+
+    It's relatively rare that single values are encoded as above.  More often, the
+    "root" data item will be an `Array` or `Map` which contains a more complex structure.For example
+    :
+
+``` using cppbor::Map;
+using cppbor::Array;
+
+std::vector<uint8_t> vec =  // ...
+    Map val("key1", Array(Map("key_a", 99 "key_b", vec), "foo"), "key2", true);
+std::vector<uint8_t> encoding = val.encode();
+```
+
+This creates a map with two entries, with `Tstr` keys "Outer1" and
+"Outer2", respectively.  The "Outer1" entry has as its value an
+`Array` containing a `Map` and a `Tstr`.  The "Outer2" entry has a
+`Bool` value.
+
+This example demonstrates how automatic conversion of C++ types to
+CppBor `Item` subclass instances is done.  Where the caller provides a
+C++ or C string, a `Tstr` entry is added.  Where the caller provides
+an integer literal or variable, a `Uint` or `Nint` is added, depending
+on whether the value is positive or negative.
+
+As an alternative, a more fluent-style API is provided for building up
+structures.  For example:
+
+```
+using cppbor::Map;
+using cppbor::Array;
+
+std::vector<uint8_t> vec =  // ...
+    Map val();
+val.add("key1", Array().add(Map().add("key_a", 99).add("key_b", vec)).add("foo")).add("key2", true);
+std::vector<uint8_t> encoding = val.encode();
+```
+
+    An advantage of this interface over the constructor -
+    based creation approach above is that it need not be done all at once
+        .The `add` methods return a reference to the object added to to allow calls to be chained,
+    but chaining is not necessary; calls can be made
+sequentially, as the data to add is available.
+
+#### `encode` methods
+
+There are several variations of `Item::encode`, all of which
+accomplish the same task but output the encoded data in different
+ways, and with somewhat different performance characteristics.  The
+provided options are:
+
+* `bool encode(uint8\_t** pos, const uint8\_t* end)` encodes into the
+  buffer referenced by the range [`*pos`, end).  `*pos` is moved.  If
+  the encoding runs out of buffer space before finishing, the method
+  returns false.  This is the most efficient way to encode, into an
+  already-allocated buffer.
+* `void encode(EncodeCallback encodeCallback)` calls `encodeCallback`
+  for each encoded byte.  It's the responsibility of the implementor
+  of the callback to behave safely in the event that the output buffer
+  (if applicable) is exhausted.  This is less efficient than the prior
+  method because it imposes an additional function call for each byte.
+* `template </*...*/> void encode(OutputIterator i)`
+  encodes into the provided iterator.  SFINAE ensures that the
+  template doesn't match for non-iterators.  The implementation
+  actually uses the callback-based method, plus has whatever overhead
+  the iterator adds.
+* `std::vector<uint8_t> encode()` creates a new std::vector, reserves
+  sufficient capacity to hold the encoding, and inserts the encoded
+  bytes with a std::pushback_iterator and the previous method.
+* `std::string toString()` does the same as the previous method, but
+  returns a string instead of a vector.
+
+### Incremental generation
+
+Incremental generation requires deeper understanding of CBOR, because
+the library can't do as much to ensure that the output is valid.  The
+basic tool for intcremental generation is the `encodeHeader`
+function.  There are two variations, one which writes into a buffer,
+and one which uses a callback.  Both simply write out the bytes of a
+header.  To construct the same map as in the above examples,
+incrementally, one might write:
+
+```
+using namespace cppbor;  // For example brevity
+
+std::vector encoding;
+auto iter = std::back_inserter(result);
+encodeHeader(MAP, 2 /* # of map entries */, iter);
+std::string s = "key1";
+encodeHeader(TSTR, s.size(), iter);
+std::copy(s.begin(), s.end(), iter);
+encodeHeader(ARRAY, 2 /* # of array entries */, iter);
+Map().add("key_a", 99).add("key_b", vec).encode(iter)
+s = "foo";
+encodeHeader(TSTR, foo.size(), iter);
+std::copy(s.begin(), s.end(), iter);
+s = "key2";
+encodeHeader(TSTR, foo.size(), iter);
+std::copy(s.begin(), s.end(), iter);
+encodeHeader(SIMPLE, TRUE, iter);
+```
+
+As the above example demonstrates, the styles can be mixed -- Note the
+creation and encoding of the inner Map using the fluent style.
+
+## Parsing
+
+CppBor also supports parsing of encoded CBOR data, with the same
+feature set as encoding.  There are two basic approaches to parsing,
+"full" and "stream"
+
+### Full parsing
+
+Full parsing means completely parsing a (possibly-compound) data
+item from a byte buffer.  The `parse` functions that do not take a
+`ParseClient` pointer do this.  They return a `ParseResult` which is a
+tuple of three values:
+
+* std::unique_ptr<Item> that points to the parsed item, or is nullptr
+  if there was a parse error.
+* const uint8_t* that points to the byte after the end of the decoded
+  item, or to the first unparseable byte in the event of an error.
+* std::string that is empty on success or contains an error message if
+  a parse error occurred.
+
+Assuming a successful parse, you can then use `Item::type()` to
+discover the type of the parsed item (e.g. MAP), and then use the
+appropriate `Item::as*()` method (e.g. `Item::asMap()`) to get a
+pointer to an interface which allows you to retrieve specific values.
+
+### Stream parsing
+
+Stream parsing is more complex, but more flexible.  To use
+StreamParsing, you must create your own subclass of `ParseClient` and
+call one of the `parse` functions that accepts it.  See the
+`ParseClient` methods docstrings for details.
+
+One unusual feature of stream parsing is that the `ParseClient`
+callback methods not only provide the parsed Item, but also pointers
+to the portion of the buffer that encode that Item.  This is useful
+if, for example, you want to find an element inside of a structure,
+and then copy the encoding of that sub-structure, without bothering to
+parse the rest.
+
+The full parser is implemented with the stream parser.
diff --git a/identity/support/include/cppbor/cppbor.h b/identity/support/include/cppbor/cppbor.h
new file mode 100644
index 0000000..a755db1
--- /dev/null
+++ b/identity/support/include/cppbor/cppbor.h
@@ -0,0 +1,827 @@
+/*
+ * 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 <cstdint>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <numeric>
+#include <string>
+#include <vector>
+
+namespace cppbor {
+
+enum MajorType : uint8_t {
+    UINT = 0 << 5,
+    NINT = 1 << 5,
+    BSTR = 2 << 5,
+    TSTR = 3 << 5,
+    ARRAY = 4 << 5,
+    MAP = 5 << 5,
+    SEMANTIC = 6 << 5,
+    SIMPLE = 7 << 5,
+};
+
+enum SimpleType {
+    BOOLEAN,
+    NULL_T,  // Only two supported, as yet.
+};
+
+enum SpecialAddlInfoValues : uint8_t {
+    FALSE = 20,
+    TRUE = 21,
+    NULL_V = 22,
+    ONE_BYTE_LENGTH = 24,
+    TWO_BYTE_LENGTH = 25,
+    FOUR_BYTE_LENGTH = 26,
+    EIGHT_BYTE_LENGTH = 27,
+};
+
+class Item;
+class Uint;
+class Nint;
+class Int;
+class Tstr;
+class Bstr;
+class Simple;
+class Bool;
+class Array;
+class Map;
+class Null;
+class Semantic;
+
+/**
+ * Returns the size of a CBOR header that contains the additional info value addlInfo.
+ */
+size_t headerSize(uint64_t addlInfo);
+
+/**
+ * Encodes a CBOR header with the specified type and additional info into the range [pos, end).
+ * Returns a pointer to one past the last byte written, or nullptr if there isn't sufficient space
+ * to write the header.
+ */
+uint8_t* encodeHeader(MajorType type, uint64_t addlInfo, uint8_t* pos, const uint8_t* end);
+
+using EncodeCallback = std::function<void(uint8_t)>;
+
+/**
+ * Encodes a CBOR header with the specified type and additional info, passing each byte in turn to
+ * encodeCallback.
+ */
+void encodeHeader(MajorType type, uint64_t addlInfo, EncodeCallback encodeCallback);
+
+/**
+ * Encodes a CBOR header with the specified type and additional info, writing each byte to the
+ * provided OutputIterator.
+ */
+template <typename OutputIterator,
+          typename = std::enable_if_t<std::is_base_of_v<
+                  std::output_iterator_tag,
+                  typename std::iterator_traits<OutputIterator>::iterator_category>>>
+void encodeHeader(MajorType type, uint64_t addlInfo, OutputIterator iter) {
+    return encodeHeader(type, addlInfo, [&](uint8_t v) { *iter++ = v; });
+}
+
+/**
+ * Item represents a CBOR-encodeable data item.  Item is an abstract interface with a set of virtual
+ * methods that allow encoding of the item or conversion to the appropriate derived type.
+ */
+class Item {
+  public:
+    virtual ~Item() {}
+
+    /**
+     * Returns the CBOR type of the item.
+     */
+    virtual MajorType type() const = 0;
+
+    // These methods safely downcast an Item to the appropriate subclass.
+    virtual const Int* asInt() const { return nullptr; }
+    virtual const Uint* asUint() const { return nullptr; }
+    virtual const Nint* asNint() const { return nullptr; }
+    virtual const Tstr* asTstr() const { return nullptr; }
+    virtual const Bstr* asBstr() const { return nullptr; }
+    virtual const Simple* asSimple() const { return nullptr; }
+    virtual const Map* asMap() const { return nullptr; }
+    virtual const Array* asArray() const { return nullptr; }
+    virtual const Semantic* asSemantic() const { return nullptr; }
+
+    /**
+     * Returns true if this is a "compound" item, i.e. one that contains one or more other items.
+     */
+    virtual bool isCompound() const { return false; }
+
+    bool operator==(const Item& other) const&;
+    bool operator!=(const Item& other) const& { return !(*this == other); }
+
+    /**
+     * Returns the number of bytes required to encode this Item into CBOR.  Note that if this is a
+     * complex Item, calling this method will require walking the whole tree.
+     */
+    virtual size_t encodedSize() const = 0;
+
+    /**
+     * Encodes the Item into buffer referenced by range [*pos, end).  Returns a pointer to one past
+     * the last position written.  Returns nullptr if there isn't enough space to encode.
+     */
+    virtual uint8_t* encode(uint8_t* pos, const uint8_t* end) const = 0;
+
+    /**
+     * Encodes the Item by passing each encoded byte to encodeCallback.
+     */
+    virtual void encode(EncodeCallback encodeCallback) const = 0;
+
+    /**
+     * Clones the Item
+     */
+    virtual std::unique_ptr<Item> clone() const = 0;
+
+    /**
+     * Encodes the Item into the provided OutputIterator.
+     */
+    template <typename OutputIterator,
+              typename = typename std::iterator_traits<OutputIterator>::iterator_category>
+    void encode(OutputIterator i) const {
+        return encode([&](uint8_t v) { *i++ = v; });
+    }
+
+    /**
+     * Encodes the Item into a new std::vector<uint8_t>.
+     */
+    std::vector<uint8_t> encode() const {
+        std::vector<uint8_t> retval;
+        retval.reserve(encodedSize());
+        encode(std::back_inserter(retval));
+        return retval;
+    }
+
+    /**
+     * Encodes the Item into a new std::string.
+     */
+    std::string toString() const {
+        std::string retval;
+        retval.reserve(encodedSize());
+        encode([&](uint8_t v) { retval.push_back(v); });
+        return retval;
+    }
+
+    /**
+     * Encodes only the header of the Item.
+     */
+    inline uint8_t* encodeHeader(uint64_t addlInfo, uint8_t* pos, const uint8_t* end) const {
+        return ::cppbor::encodeHeader(type(), addlInfo, pos, end);
+    }
+
+    /**
+     * Encodes only the header of the Item.
+     */
+    inline void encodeHeader(uint64_t addlInfo, EncodeCallback encodeCallback) const {
+        ::cppbor::encodeHeader(type(), addlInfo, encodeCallback);
+    }
+};
+
+/**
+ * Int is an abstraction that allows Uint and Nint objects to be manipulated without caring about
+ * the sign.
+ */
+class Int : public Item {
+  public:
+    bool operator==(const Int& other) const& { return value() == other.value(); }
+
+    virtual int64_t value() const = 0;
+
+    const Int* asInt() const override { return this; }
+};
+
+/**
+ * Uint is a concrete Item that implements CBOR major type 0.
+ */
+class Uint : public Int {
+  public:
+    static constexpr MajorType kMajorType = UINT;
+
+    explicit Uint(uint64_t v) : mValue(v) {}
+
+    bool operator==(const Uint& other) const& { return mValue == other.mValue; }
+
+    MajorType type() const override { return kMajorType; }
+    const Uint* asUint() const override { return this; }
+
+    size_t encodedSize() const override { return headerSize(mValue); }
+
+    int64_t value() const override { return mValue; }
+    uint64_t unsignedValue() const { return mValue; }
+
+    using Item::encode;
+    uint8_t* encode(uint8_t* pos, const uint8_t* end) const override {
+        return encodeHeader(mValue, pos, end);
+    }
+    void encode(EncodeCallback encodeCallback) const override {
+        encodeHeader(mValue, encodeCallback);
+    }
+
+    virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Uint>(mValue); }
+
+  private:
+    uint64_t mValue;
+};
+
+/**
+ * Nint is a concrete Item that implements CBOR major type 1.
+
+ * Note that it is incapable of expressing the full range of major type 1 values, becaue it can only
+ * express values that fall into the range [std::numeric_limits<int64_t>::min(), -1].  It cannot
+ * express values in the range [std::numeric_limits<int64_t>::min() - 1,
+ * -std::numeric_limits<uint64_t>::max()].
+ */
+class Nint : public Int {
+  public:
+    static constexpr MajorType kMajorType = NINT;
+
+    explicit Nint(int64_t v);
+
+    bool operator==(const Nint& other) const& { return mValue == other.mValue; }
+
+    MajorType type() const override { return kMajorType; }
+    const Nint* asNint() const override { return this; }
+    size_t encodedSize() const override { return headerSize(addlInfo()); }
+
+    int64_t value() const override { return mValue; }
+
+    using Item::encode;
+    uint8_t* encode(uint8_t* pos, const uint8_t* end) const override {
+        return encodeHeader(addlInfo(), pos, end);
+    }
+    void encode(EncodeCallback encodeCallback) const override {
+        encodeHeader(addlInfo(), encodeCallback);
+    }
+
+    virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Nint>(mValue); }
+
+  private:
+    uint64_t addlInfo() const { return -1ll - mValue; }
+
+    int64_t mValue;
+};
+
+/**
+ * Bstr is a concrete Item that implements major type 2.
+ */
+class Bstr : public Item {
+  public:
+    static constexpr MajorType kMajorType = BSTR;
+
+    // Construct from a vector
+    explicit Bstr(std::vector<uint8_t> v) : mValue(std::move(v)) {}
+
+    // Construct from a string
+    explicit Bstr(const std::string& v)
+        : mValue(reinterpret_cast<const uint8_t*>(v.data()),
+                 reinterpret_cast<const uint8_t*>(v.data()) + v.size()) {}
+
+    // Construct from a pointer/size pair
+    explicit Bstr(const std::pair<const uint8_t*, size_t>& buf)
+        : mValue(buf.first, buf.first + buf.second) {}
+
+    // Construct from a pair of iterators
+    template <typename I1, typename I2,
+              typename = typename std::iterator_traits<I1>::iterator_category,
+              typename = typename std::iterator_traits<I2>::iterator_category>
+    explicit Bstr(const std::pair<I1, I2>& pair) : mValue(pair.first, pair.second) {}
+
+    // Construct from an iterator range.
+    template <typename I1, typename I2,
+              typename = typename std::iterator_traits<I1>::iterator_category,
+              typename = typename std::iterator_traits<I2>::iterator_category>
+    Bstr(I1 begin, I2 end) : mValue(begin, end) {}
+
+    bool operator==(const Bstr& other) const& { return mValue == other.mValue; }
+
+    MajorType type() const override { return kMajorType; }
+    const Bstr* asBstr() const override { return this; }
+    size_t encodedSize() const override { return headerSize(mValue.size()) + mValue.size(); }
+    using Item::encode;
+    uint8_t* encode(uint8_t* pos, const uint8_t* end) const override;
+    void encode(EncodeCallback encodeCallback) const override {
+        encodeHeader(mValue.size(), encodeCallback);
+        encodeValue(encodeCallback);
+    }
+
+    const std::vector<uint8_t>& value() const { return mValue; }
+
+    virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Bstr>(mValue); }
+
+  private:
+    void encodeValue(EncodeCallback encodeCallback) const;
+
+    std::vector<uint8_t> mValue;
+};
+
+/**
+ * Bstr is a concrete Item that implements major type 3.
+ */
+class Tstr : public Item {
+  public:
+    static constexpr MajorType kMajorType = TSTR;
+
+    // Construct from a string
+    explicit Tstr(std::string v) : mValue(std::move(v)) {}
+
+    // Construct from a string_view
+    explicit Tstr(const std::string_view& v) : mValue(v) {}
+
+    // Construct from a C string
+    explicit Tstr(const char* v) : mValue(std::string(v)) {}
+
+    // Construct from a pair of iterators
+    template <typename I1, typename I2,
+              typename = typename std::iterator_traits<I1>::iterator_category,
+              typename = typename std::iterator_traits<I2>::iterator_category>
+    explicit Tstr(const std::pair<I1, I2>& pair) : mValue(pair.first, pair.second) {}
+
+    // Construct from an iterator range
+    template <typename I1, typename I2,
+              typename = typename std::iterator_traits<I1>::iterator_category,
+              typename = typename std::iterator_traits<I2>::iterator_category>
+    Tstr(I1 begin, I2 end) : mValue(begin, end) {}
+
+    bool operator==(const Tstr& other) const& { return mValue == other.mValue; }
+
+    MajorType type() const override { return kMajorType; }
+    const Tstr* asTstr() const override { return this; }
+    size_t encodedSize() const override { return headerSize(mValue.size()) + mValue.size(); }
+    using Item::encode;
+    uint8_t* encode(uint8_t* pos, const uint8_t* end) const override;
+    void encode(EncodeCallback encodeCallback) const override {
+        encodeHeader(mValue.size(), encodeCallback);
+        encodeValue(encodeCallback);
+    }
+
+    const std::string& value() const { return mValue; }
+
+    virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Tstr>(mValue); }
+
+  private:
+    void encodeValue(EncodeCallback encodeCallback) const;
+
+    std::string mValue;
+};
+
+/**
+ * CompoundItem is an abstract Item that provides common functionality for Items that contain other
+ * items, i.e. Arrays (CBOR type 4) and Maps (CBOR type 5).
+ */
+class CompoundItem : public Item {
+  public:
+    bool operator==(const CompoundItem& other) const&;
+
+    virtual size_t size() const { return mEntries.size(); }
+
+    bool isCompound() const override { return true; }
+
+    size_t encodedSize() const override {
+        return std::accumulate(mEntries.begin(), mEntries.end(), headerSize(size()),
+                               [](size_t sum, auto& entry) { return sum + entry->encodedSize(); });
+    }
+
+    using Item::encode;  // Make base versions visible.
+    uint8_t* encode(uint8_t* pos, const uint8_t* end) const override;
+    void encode(EncodeCallback encodeCallback) const override;
+
+    virtual uint64_t addlInfo() const = 0;
+
+  protected:
+    std::vector<std::unique_ptr<Item>> mEntries;
+};
+
+/*
+ * Array is a concrete Item that implements CBOR major type 4.
+ *
+ * Note that Arrays are not copyable.  This is because copying them is expensive and making them
+ * move-only ensures that they're never copied accidentally.  If you actually want to copy an Array,
+ * use the clone() method.
+ */
+class Array : public CompoundItem {
+  public:
+    static constexpr MajorType kMajorType = ARRAY;
+
+    Array() = default;
+    Array(const Array& other) = delete;
+    Array(Array&&) = default;
+    Array& operator=(const Array&) = delete;
+    Array& operator=(Array&&) = default;
+
+    /**
+     * Construct an Array from a variable number of arguments of different types.  See
+     * details::makeItem below for details on what types may be provided.  In general, this accepts
+     * all of the types you'd expect and doest the things you'd expect (integral values are addes as
+     * Uint or Nint, std::string and char* are added as Tstr, bools are added as Bool, etc.).
+     */
+    template <typename... Args, typename Enable>
+    Array(Args&&... args);
+
+    /**
+     * Append a single element to the Array, of any compatible type.
+     */
+    template <typename T>
+    Array& add(T&& v) &;
+    template <typename T>
+    Array&& add(T&& v) &&;
+
+    const std::unique_ptr<Item>& operator[](size_t index) const { return mEntries[index]; }
+    std::unique_ptr<Item>& operator[](size_t index) { return mEntries[index]; }
+
+    MajorType type() const override { return kMajorType; }
+    const Array* asArray() const override { return this; }
+
+    virtual std::unique_ptr<Item> clone() const override;
+
+    uint64_t addlInfo() const override { return size(); }
+};
+
+/*
+ * Map is a concrete Item that implements CBOR major type 5.
+ *
+ * Note that Maps are not copyable.  This is because copying them is expensive and making them
+ * move-only ensures that they're never copied accidentally.  If you actually want to copy a
+ * Map, use the clone() method.
+ */
+class Map : public CompoundItem {
+  public:
+    static constexpr MajorType kMajorType = MAP;
+
+    Map() = default;
+    Map(const Map& other) = delete;
+    Map(Map&&) = default;
+    Map& operator=(const Map& other) = delete;
+    Map& operator=(Map&&) = default;
+
+    /**
+     * Construct a Map from a variable number of arguments of different types.  An even number of
+     * arguments must be provided (this is verified statically). See details::makeItem below for
+     * details on what types may be provided.  In general, this accepts all of the types you'd
+     * expect and doest the things you'd expect (integral values are addes as Uint or Nint,
+     * std::string and char* are added as Tstr, bools are added as Bool, etc.).
+     */
+    template <typename... Args, typename Enable>
+    Map(Args&&... args);
+
+    /**
+     * Append a key/value pair to the Map, of any compatible types.
+     */
+    template <typename Key, typename Value>
+    Map& add(Key&& key, Value&& value) &;
+    template <typename Key, typename Value>
+    Map&& add(Key&& key, Value&& value) &&;
+
+    size_t size() const override {
+        assertInvariant();
+        return mEntries.size() / 2;
+    }
+
+    template <typename Key, typename Enable>
+    std::pair<std::unique_ptr<Item>&, bool> get(Key key);
+
+    std::pair<const std::unique_ptr<Item>&, const std::unique_ptr<Item>&> operator[](
+            size_t index) const {
+        assertInvariant();
+        return {mEntries[index * 2], mEntries[index * 2 + 1]};
+    }
+
+    std::pair<std::unique_ptr<Item>&, std::unique_ptr<Item>&> operator[](size_t index) {
+        assertInvariant();
+        return {mEntries[index * 2], mEntries[index * 2 + 1]};
+    }
+
+    MajorType type() const override { return kMajorType; }
+    const Map* asMap() const override { return this; }
+
+    virtual std::unique_ptr<Item> clone() const override;
+
+    uint64_t addlInfo() const override { return size(); }
+
+  private:
+    void assertInvariant() const;
+};
+
+class Semantic : public CompoundItem {
+  public:
+    static constexpr MajorType kMajorType = SEMANTIC;
+
+    template <typename T>
+    explicit Semantic(uint64_t value, T&& child);
+
+    Semantic(const Semantic& other) = delete;
+    Semantic(Semantic&&) = default;
+    Semantic& operator=(const Semantic& other) = delete;
+    Semantic& operator=(Semantic&&) = default;
+
+    size_t size() const override {
+        assertInvariant();
+        return 1;
+    }
+
+    size_t encodedSize() const override {
+        return std::accumulate(mEntries.begin(), mEntries.end(), headerSize(mValue),
+                               [](size_t sum, auto& entry) { return sum + entry->encodedSize(); });
+    }
+
+    MajorType type() const override { return kMajorType; }
+    const Semantic* asSemantic() const override { return this; }
+
+    const std::unique_ptr<Item>& child() const {
+        assertInvariant();
+        return mEntries[0];
+    }
+
+    std::unique_ptr<Item>& child() {
+        assertInvariant();
+        return mEntries[0];
+    }
+
+    uint64_t value() const { return mValue; }
+
+    uint64_t addlInfo() const override { return value(); }
+
+    virtual std::unique_ptr<Item> clone() const override {
+        assertInvariant();
+        return std::make_unique<Semantic>(mValue, mEntries[0]->clone());
+    }
+
+  protected:
+    Semantic() = default;
+    Semantic(uint64_t value) : mValue(value) {}
+    uint64_t mValue;
+
+  private:
+    void assertInvariant() const;
+};
+
+/**
+ * Simple is abstract Item that implements CBOR major type 7.  It is intended to be subclassed to
+ * create concrete Simple types.  At present only Bool is provided.
+ */
+class Simple : public Item {
+  public:
+    static constexpr MajorType kMajorType = SIMPLE;
+
+    bool operator==(const Simple& other) const&;
+
+    virtual SimpleType simpleType() const = 0;
+    MajorType type() const override { return kMajorType; }
+
+    const Simple* asSimple() const override { return this; }
+
+    virtual const Bool* asBool() const { return nullptr; };
+    virtual const Null* asNull() const { return nullptr; };
+};
+
+/**
+ * Bool is a concrete type that implements CBOR major type 7, with additional item values for TRUE
+ * and FALSE.
+ */
+class Bool : public Simple {
+  public:
+    static constexpr SimpleType kSimpleType = BOOLEAN;
+
+    explicit Bool(bool v) : mValue(v) {}
+
+    bool operator==(const Bool& other) const& { return mValue == other.mValue; }
+
+    SimpleType simpleType() const override { return kSimpleType; }
+    const Bool* asBool() const override { return this; }
+
+    size_t encodedSize() const override { return 1; }
+
+    using Item::encode;
+    uint8_t* encode(uint8_t* pos, const uint8_t* end) const override {
+        return encodeHeader(mValue ? TRUE : FALSE, pos, end);
+    }
+    void encode(EncodeCallback encodeCallback) const override {
+        encodeHeader(mValue ? TRUE : FALSE, encodeCallback);
+    }
+
+    bool value() const { return mValue; }
+
+    virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Bool>(mValue); }
+
+  private:
+    bool mValue;
+};
+
+/**
+ * Null is a concrete type that implements CBOR major type 7, with additional item value for NULL
+ */
+class Null : public Simple {
+  public:
+    static constexpr SimpleType kSimpleType = NULL_T;
+
+    explicit Null() {}
+
+    SimpleType simpleType() const override { return kSimpleType; }
+    const Null* asNull() const override { return this; }
+
+    size_t encodedSize() const override { return 1; }
+
+    using Item::encode;
+    uint8_t* encode(uint8_t* pos, const uint8_t* end) const override {
+        return encodeHeader(NULL_V, pos, end);
+    }
+    void encode(EncodeCallback encodeCallback) const override {
+        encodeHeader(NULL_V, encodeCallback);
+    }
+
+    virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Null>(); }
+};
+
+template <typename T>
+std::unique_ptr<T> downcastItem(std::unique_ptr<Item>&& v) {
+    static_assert(std::is_base_of_v<Item, T> && !std::is_abstract_v<T>,
+                  "returned type is not an Item or is an abstract class");
+    if (v && T::kMajorType == v->type()) {
+        if constexpr (std::is_base_of_v<Simple, T>) {
+            if (T::kSimpleType != v->asSimple()->simpleType()) {
+                return nullptr;
+            }
+        }
+        return std::unique_ptr<T>(static_cast<T*>(v.release()));
+    } else {
+        return nullptr;
+    }
+}
+
+/**
+ * Details. Mostly you shouldn't have to look below, except perhaps at the docstring for makeItem.
+ */
+namespace details {
+
+template <typename T, typename V, typename Enable = void>
+struct is_iterator_pair_over : public std::false_type {};
+
+template <typename I1, typename I2, typename V>
+struct is_iterator_pair_over<
+        std::pair<I1, I2>, V,
+        typename std::enable_if_t<std::is_same_v<V, typename std::iterator_traits<I1>::value_type>>>
+    : public std::true_type {};
+
+template <typename T, typename V, typename Enable = void>
+struct is_unique_ptr_of_subclass_of_v : public std::false_type {};
+
+template <typename T, typename P>
+struct is_unique_ptr_of_subclass_of_v<T, std::unique_ptr<P>,
+                                      typename std::enable_if_t<std::is_base_of_v<T, P>>>
+    : public std::true_type {};
+
+/* check if type is one of std::string (1), std::string_view (2), null-terminated char* (3) or pair
+ *     of iterators (4)*/
+template <typename T, typename Enable = void>
+struct is_text_type_v : public std::false_type {};
+
+template <typename T>
+struct is_text_type_v<
+        T, typename std::enable_if_t<
+                   /* case 1 */  //
+                   std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, std::string>
+                   /* case 2 */  //
+                   || std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, std::string_view>
+                   /* case 3 */                                                 //
+                   || std::is_same_v<std::remove_cv_t<std::decay_t<T>>, char*>  //
+                   || std::is_same_v<std::remove_cv_t<std::decay_t<T>>, const char*>
+                   /* case 4 */
+                   || details::is_iterator_pair_over<T, char>::value>> : public std::true_type {};
+
+/**
+ * Construct a unique_ptr<Item> from many argument types. Accepts:
+ *
+ * (a) booleans;
+ * (b) integers, all sizes and signs;
+ * (c) text strings, as defined by is_text_type_v above;
+ * (d) byte strings, as std::vector<uint8_t>(d1), pair of iterators (d2) or pair<uint8_t*, size_T>
+ *     (d3); and
+ * (e) Item subclass instances, including Array and Map.  Items may be provided by naked pointer
+ *     (e1), unique_ptr (e2), reference (e3) or value (e3).  If provided by reference or value, will
+ *     be moved if possible.  If provided by pointer, ownership is taken.
+ * (f) null pointer;
+ */
+template <typename T>
+std::unique_ptr<Item> makeItem(T v) {
+    Item* p = nullptr;
+    if constexpr (/* case a */ std::is_same_v<T, bool>) {
+        p = new Bool(v);
+    } else if constexpr (/* case b */ std::is_integral_v<T>) {  // b
+        if (v < 0) {
+            p = new Nint(v);
+        } else {
+            p = new Uint(static_cast<uint64_t>(v));
+        }
+    } else if constexpr (/* case c */  //
+                         details::is_text_type_v<T>::value) {
+        p = new Tstr(v);
+    } else if constexpr (/* case d1 */  //
+                         std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>,
+                                        std::vector<uint8_t>>
+                         /* case d2 */  //
+                         || details::is_iterator_pair_over<T, uint8_t>::value
+                         /* case d3 */  //
+                         || std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>,
+                                           std::pair<uint8_t*, size_t>>) {
+        p = new Bstr(v);
+    } else if constexpr (/* case e1 */  //
+                         std::is_pointer_v<T> &&
+                         std::is_base_of_v<Item, std::remove_pointer_t<T>>) {
+        p = v;
+    } else if constexpr (/* case e2 */  //
+                         details::is_unique_ptr_of_subclass_of_v<Item, T>::value) {
+        p = v.release();
+    } else if constexpr (/* case e3 */  //
+                         std::is_base_of_v<Item, T>) {
+        p = new T(std::move(v));
+    } else if constexpr (/* case f */ std::is_null_pointer_v<T>) {
+        p = new Null();
+    } else {
+        // It's odd that this can't be static_assert(false), since it shouldn't be evaluated if one
+        // of the above ifs matches.  But static_assert(false) always triggers.
+        static_assert(std::is_same_v<T, bool>, "makeItem called with unsupported type");
+    }
+    return std::unique_ptr<Item>(p);
+}
+
+}  // namespace details
+
+template <typename... Args,
+          /* Prevent use as copy ctor */ typename = std::enable_if_t<
+                  (sizeof...(Args)) != 1 ||
+                  !(std::is_same_v<Array, std::remove_cv_t<std::remove_reference_t<Args>>> || ...)>>
+Array::Array(Args&&... args) {
+    mEntries.reserve(sizeof...(args));
+    (mEntries.push_back(details::makeItem(std::forward<Args>(args))), ...);
+}
+
+template <typename T>
+Array& Array::add(T&& v) & {
+    mEntries.push_back(details::makeItem(std::forward<T>(v)));
+    return *this;
+}
+
+template <typename T>
+Array&& Array::add(T&& v) && {
+    mEntries.push_back(details::makeItem(std::forward<T>(v)));
+    return std::move(*this);
+}
+
+template <typename... Args,
+          /* Prevent use as copy ctor */ typename = std::enable_if_t<(sizeof...(Args)) != 1>>
+Map::Map(Args&&... args) {
+    static_assert((sizeof...(Args)) % 2 == 0, "Map must have an even number of entries");
+    mEntries.reserve(sizeof...(args));
+    (mEntries.push_back(details::makeItem(std::forward<Args>(args))), ...);
+}
+
+template <typename Key, typename Value>
+Map& Map::add(Key&& key, Value&& value) & {
+    mEntries.push_back(details::makeItem(std::forward<Key>(key)));
+    mEntries.push_back(details::makeItem(std::forward<Value>(value)));
+    return *this;
+}
+
+template <typename Key, typename Value>
+Map&& Map::add(Key&& key, Value&& value) && {
+    this->add(std::forward<Key>(key), std::forward<Value>(value));
+    return std::move(*this);
+}
+
+template <typename Key, typename = std::enable_if_t<std::is_integral_v<Key> ||
+                                                    details::is_text_type_v<Key>::value>>
+std::pair<std::unique_ptr<Item>&, bool> Map::get(Key key) {
+    assertInvariant();
+    auto keyItem = details::makeItem(key);
+    for (size_t i = 0; i < mEntries.size(); i += 2) {
+        if (*keyItem == *mEntries[i]) {
+            return {mEntries[i + 1], true};
+        }
+    }
+    return {keyItem, false};
+}
+
+template <typename T>
+Semantic::Semantic(uint64_t value, T&& child) : mValue(value) {
+    mEntries.reserve(1);
+    mEntries.push_back(details::makeItem(std::forward<T>(child)));
+}
+
+}  // namespace cppbor
diff --git a/identity/support/include/cppbor/cppbor_parse.h b/identity/support/include/cppbor/cppbor_parse.h
new file mode 100644
index 0000000..66cd5a3
--- /dev/null
+++ b/identity/support/include/cppbor/cppbor_parse.h
@@ -0,0 +1,133 @@
+/*
+ * 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 "cppbor.h"
+
+namespace cppbor {
+
+using ParseResult = std::tuple<std::unique_ptr<Item> /* result */, const uint8_t* /* newPos */,
+                               std::string /* errMsg */>;
+
+/**
+ * Parse the first CBOR data item (possibly compound) from the range [begin, end).
+ *
+ * Returns a tuple of Item pointer, buffer pointer and error message.  If parsing is successful, the
+ * Item pointer is non-null, the buffer pointer points to the first byte after the
+ * successfully-parsed item and the error message string is empty.  If parsing fails, the Item
+ * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte
+ * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is
+ * too large for the remining buffer, etc.) and the string contains an error message describing the
+ * problem encountered.
+ */
+ParseResult parse(const uint8_t* begin, const uint8_t* end);
+
+/**
+ * Parse the first CBOR data item (possibly compound) from the byte vector.
+ *
+ * Returns a tuple of Item pointer, buffer pointer and error message.  If parsing is successful, the
+ * Item pointer is non-null, the buffer pointer points to the first byte after the
+ * successfully-parsed item and the error message string is empty.  If parsing fails, the Item
+ * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte
+ * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is
+ * too large for the remining buffer, etc.) and the string contains an error message describing the
+ * problem encountered.
+ */
+inline ParseResult parse(const std::vector<uint8_t>& encoding) {
+    return parse(encoding.data(), encoding.data() + encoding.size());
+}
+
+/**
+ * Parse the first CBOR data item (possibly compound) from the range [begin, begin + size).
+ *
+ * Returns a tuple of Item pointer, buffer pointer and error message.  If parsing is successful, the
+ * Item pointer is non-null, the buffer pointer points to the first byte after the
+ * successfully-parsed item and the error message string is empty.  If parsing fails, the Item
+ * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte
+ * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is
+ * too large for the remining buffer, etc.) and the string contains an error message describing the
+ * problem encountered.
+ */
+inline ParseResult parse(const uint8_t* begin, size_t size) {
+    return parse(begin, begin + size);
+}
+
+class ParseClient;
+
+/**
+ * Parse the CBOR data in the range [begin, end) in streaming fashion, calling methods on the
+ * provided ParseClient when elements are found.
+ */
+void parse(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient);
+
+/**
+ * Parse the CBOR data in the vector in streaming fashion, calling methods on the
+ * provided ParseClient when elements are found.
+ */
+inline void parse(const std::vector<uint8_t>& encoding, ParseClient* parseClient) {
+    return parse(encoding.data(), encoding.data() + encoding.size(), parseClient);
+}
+
+/**
+ * A pure interface that callers of the streaming parse functions must implement.
+ */
+class ParseClient {
+  public:
+    virtual ~ParseClient() {}
+
+    /**
+     * Called when an item is found.  The Item pointer points to the found item; use type() and
+     * the appropriate as*() method to examine the value.  hdrBegin points to the first byte of the
+     * header, valueBegin points to the first byte of the value and end points one past the end of
+     * the item.  In the case of header-only items, such as integers, and compound items (ARRAY,
+     * MAP or SEMANTIC) whose end has not yet been found, valueBegin and end are equal and point to
+     * the byte past the header.
+     *
+     * Note that for compound types (ARRAY, MAP, and SEMANTIC), the Item will have no content.  For
+     * Map and Array items, the size() method will return a correct value, but the index operators
+     * are unsafe, and the object cannot be safely compared with another Array/Map.
+     *
+     * The method returns a ParseClient*.  In most cases "return this;" will be the right answer,
+     * but a different ParseClient may be returned, which the parser will begin using. If the method
+     * returns nullptr, parsing will be aborted immediately.
+     */
+    virtual ParseClient* item(std::unique_ptr<Item>& item, const uint8_t* hdrBegin,
+                              const uint8_t* valueBegin, const uint8_t* end) = 0;
+
+    /**
+     * Called when the end of a compound item (MAP or ARRAY) is found.  The item argument will be
+     * the same one passed to the item() call -- and may be empty if item() moved its value out.
+     * hdrBegin, valueBegin and end point to the beginning of the item header, the beginning of the
+     * first contained value, and one past the end of the last contained value, respectively.
+     *
+     * Note that the Item will have no content.
+     *
+     * As with item(), itemEnd() can change the ParseClient by returning a different one, or end the
+     * parsing by returning nullptr;
+     */
+    virtual ParseClient* itemEnd(std::unique_ptr<Item>& item, const uint8_t* hdrBegin,
+                                 const uint8_t* valueBegin, const uint8_t* end) = 0;
+
+    /**
+     * Called when parsing encounters an error.  position is set to the first unparsed byte (one
+     * past the last successfully-parsed byte) and errorMessage contains an message explaining what
+     * sort of error occurred.
+     */
+    virtual void error(const uint8_t* position, const std::string& errorMessage) = 0;
+};
+
+}  // namespace cppbor
diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp
new file mode 100644
index 0000000..7d93a4b
--- /dev/null
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -0,0 +1,1815 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IdentityCredentialSupport"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#define _POSIX_C_SOURCE 199309L
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <time.h>
+#include <iomanip>
+
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hkdf.h>
+#include <openssl/hmac.h>
+#include <openssl/objects.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+namespace android {
+namespace hardware {
+namespace identity {
+namespace support {
+
+using ::std::pair;
+using ::std::unique_ptr;
+
+// ---------------------------------------------------------------------------
+// Miscellaneous utilities.
+// ---------------------------------------------------------------------------
+
+void hexdump(const string& name, const vector<uint8_t>& data) {
+    fprintf(stderr, "%s: dumping %zd bytes\n", name.c_str(), data.size());
+    size_t n, m, o;
+    for (n = 0; n < data.size(); n += 16) {
+        fprintf(stderr, "%04zx  ", n);
+        for (m = 0; m < 16 && n + m < data.size(); m++) {
+            fprintf(stderr, "%02x ", data[n + m]);
+        }
+        for (o = m; o < 16; o++) {
+            fprintf(stderr, "   ");
+        }
+        fprintf(stderr, " ");
+        for (m = 0; m < 16 && n + m < data.size(); m++) {
+            int c = data[n + m];
+            fprintf(stderr, "%c", isprint(c) ? c : '.');
+        }
+        fprintf(stderr, "\n");
+    }
+    fprintf(stderr, "\n");
+}
+
+string encodeHex(const uint8_t* data, size_t dataLen) {
+    static const char hexDigits[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+                                       '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+    string ret;
+    ret.resize(dataLen * 2);
+    for (size_t n = 0; n < dataLen; n++) {
+        uint8_t byte = data[n];
+        ret[n * 2 + 0] = hexDigits[byte >> 4];
+        ret[n * 2 + 1] = hexDigits[byte & 0x0f];
+    }
+
+    return ret;
+}
+
+string encodeHex(const string& str) {
+    return encodeHex(reinterpret_cast<const uint8_t*>(str.data()), str.size());
+}
+
+string encodeHex(const vector<uint8_t>& data) {
+    return encodeHex(data.data(), data.size());
+}
+
+// Returns -1 on error, otherwise an integer in the range 0 through 15, both inclusive.
+int parseHexDigit(char hexDigit) {
+    if (hexDigit >= '0' && hexDigit <= '9') {
+        return int(hexDigit) - '0';
+    } else if (hexDigit >= 'a' && hexDigit <= 'f') {
+        return int(hexDigit) - 'a' + 10;
+    } else if (hexDigit >= 'A' && hexDigit <= 'F') {
+        return int(hexDigit) - 'A' + 10;
+    }
+    return -1;
+}
+
+optional<vector<uint8_t>> decodeHex(const string& hexEncoded) {
+    vector<uint8_t> out;
+    size_t hexSize = hexEncoded.size();
+    if ((hexSize & 1) != 0) {
+        LOG(ERROR) << "Size of data cannot be odd";
+        return {};
+    }
+
+    out.resize(hexSize / 2);
+    for (size_t n = 0; n < hexSize / 2; n++) {
+        int upperNibble = parseHexDigit(hexEncoded[n * 2]);
+        int lowerNibble = parseHexDigit(hexEncoded[n * 2 + 1]);
+        if (upperNibble == -1 || lowerNibble == -1) {
+            LOG(ERROR) << "Invalid hex digit at position " << n;
+            return {};
+        }
+        out[n] = (upperNibble << 4) + lowerNibble;
+    }
+
+    return out;
+}
+
+// ---------------------------------------------------------------------------
+// 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>;
+
+// bool getRandom(size_t numBytes, vector<uint8_t>& output) {
+optional<vector<uint8_t>> getRandom(size_t numBytes) {
+    vector<uint8_t> output;
+    output.resize(numBytes);
+    if (RAND_bytes(output.data(), numBytes) != 1) {
+        LOG(ERROR) << "RAND_bytes: failed getting " << numBytes << " random";
+        return {};
+    }
+    return output;
+}
+
+optional<vector<uint8_t>> decryptAes128Gcm(const vector<uint8_t>& key,
+                                           const vector<uint8_t>& encryptedData,
+                                           const vector<uint8_t>& additionalAuthenticatedData) {
+    int cipherTextSize = int(encryptedData.size()) - kAesGcmIvSize - kAesGcmTagSize;
+    if (cipherTextSize < 0) {
+        LOG(ERROR) << "encryptedData too small";
+        return {};
+    }
+    unsigned char* nonce = (unsigned char*)encryptedData.data();
+    unsigned char* cipherText = nonce + kAesGcmIvSize;
+    unsigned char* tag = cipherText + cipherTextSize;
+
+    vector<uint8_t> plainText;
+    plainText.resize(cipherTextSize);
+
+    auto ctx = EvpCipherCtxPtr(EVP_CIPHER_CTX_new());
+    if (ctx.get() == nullptr) {
+        LOG(ERROR) << "EVP_CIPHER_CTX_new: failed";
+        return {};
+    }
+
+    if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_gcm(), NULL, NULL, NULL) != 1) {
+        LOG(ERROR) << "EVP_DecryptInit_ex: failed";
+        return {};
+    }
+
+    if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, kAesGcmIvSize, NULL) != 1) {
+        LOG(ERROR) << "EVP_CIPHER_CTX_ctrl: failed setting nonce length";
+        return {};
+    }
+
+    if (EVP_DecryptInit_ex(ctx.get(), NULL, NULL, (unsigned char*)key.data(), nonce) != 1) {
+        LOG(ERROR) << "EVP_DecryptInit_ex: failed";
+        return {};
+    }
+
+    int numWritten;
+    if (additionalAuthenticatedData.size() > 0) {
+        if (EVP_DecryptUpdate(ctx.get(), NULL, &numWritten,
+                              (unsigned char*)additionalAuthenticatedData.data(),
+                              additionalAuthenticatedData.size()) != 1) {
+            LOG(ERROR) << "EVP_DecryptUpdate: failed for additionalAuthenticatedData";
+            return {};
+        }
+        if ((size_t)numWritten != additionalAuthenticatedData.size()) {
+            LOG(ERROR) << "EVP_DecryptUpdate: Unexpected outl=" << numWritten << " (expected "
+                       << additionalAuthenticatedData.size() << ") for additionalAuthenticatedData";
+            return {};
+        }
+    }
+
+    if (EVP_DecryptUpdate(ctx.get(), (unsigned char*)plainText.data(), &numWritten, cipherText,
+                          cipherTextSize) != 1) {
+        LOG(ERROR) << "EVP_DecryptUpdate: failed";
+        return {};
+    }
+    if (numWritten != cipherTextSize) {
+        LOG(ERROR) << "EVP_DecryptUpdate: Unexpected outl=" << numWritten << " (expected "
+                   << cipherTextSize << ")";
+        return {};
+    }
+
+    if (!EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, kAesGcmTagSize, tag)) {
+        LOG(ERROR) << "EVP_CIPHER_CTX_ctrl: failed setting expected tag";
+        return {};
+    }
+
+    int ret = EVP_DecryptFinal_ex(ctx.get(), (unsigned char*)plainText.data() + numWritten,
+                                  &numWritten);
+    if (ret != 1) {
+        LOG(ERROR) << "EVP_DecryptFinal_ex: failed";
+        return {};
+    }
+    if (numWritten != 0) {
+        LOG(ERROR) << "EVP_DecryptFinal_ex: Unexpected non-zero outl=" << numWritten;
+        return {};
+    }
+
+    return plainText;
+}
+
+optional<vector<uint8_t>> encryptAes128Gcm(const vector<uint8_t>& key, const vector<uint8_t>& nonce,
+                                           const vector<uint8_t>& data,
+                                           const vector<uint8_t>& additionalAuthenticatedData) {
+    if (key.size() != kAes128GcmKeySize) {
+        LOG(ERROR) << "key is not kAes128GcmKeySize bytes";
+        return {};
+    }
+    if (nonce.size() != kAesGcmIvSize) {
+        LOG(ERROR) << "nonce is not kAesGcmIvSize bytes";
+        return {};
+    }
+
+    // The result is the nonce (kAesGcmIvSize bytes), the ciphertext, and
+    // finally the tag (kAesGcmTagSize bytes).
+    vector<uint8_t> encryptedData;
+    encryptedData.resize(data.size() + kAesGcmIvSize + kAesGcmTagSize);
+    unsigned char* noncePtr = (unsigned char*)encryptedData.data();
+    unsigned char* cipherText = noncePtr + kAesGcmIvSize;
+    unsigned char* tag = cipherText + data.size();
+    memcpy(noncePtr, nonce.data(), kAesGcmIvSize);
+
+    auto ctx = EvpCipherCtxPtr(EVP_CIPHER_CTX_new());
+    if (ctx.get() == nullptr) {
+        LOG(ERROR) << "EVP_CIPHER_CTX_new: failed";
+        return {};
+    }
+
+    if (EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_gcm(), NULL, NULL, NULL) != 1) {
+        LOG(ERROR) << "EVP_EncryptInit_ex: failed";
+        return {};
+    }
+
+    if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, kAesGcmIvSize, NULL) != 1) {
+        LOG(ERROR) << "EVP_CIPHER_CTX_ctrl: failed setting nonce length";
+        return {};
+    }
+
+    if (EVP_EncryptInit_ex(ctx.get(), NULL, NULL, (unsigned char*)key.data(),
+                           (unsigned char*)nonce.data()) != 1) {
+        LOG(ERROR) << "EVP_EncryptInit_ex: failed";
+        return {};
+    }
+
+    int numWritten;
+    if (additionalAuthenticatedData.size() > 0) {
+        if (EVP_EncryptUpdate(ctx.get(), NULL, &numWritten,
+                              (unsigned char*)additionalAuthenticatedData.data(),
+                              additionalAuthenticatedData.size()) != 1) {
+            LOG(ERROR) << "EVP_EncryptUpdate: failed for additionalAuthenticatedData";
+            return {};
+        }
+        if ((size_t)numWritten != additionalAuthenticatedData.size()) {
+            LOG(ERROR) << "EVP_EncryptUpdate: Unexpected outl=" << numWritten << " (expected "
+                       << additionalAuthenticatedData.size() << ") for additionalAuthenticatedData";
+            return {};
+        }
+    }
+
+    if (data.size() > 0) {
+        if (EVP_EncryptUpdate(ctx.get(), cipherText, &numWritten, (unsigned char*)data.data(),
+                              data.size()) != 1) {
+            LOG(ERROR) << "EVP_EncryptUpdate: failed";
+            return {};
+        }
+        if ((size_t)numWritten != data.size()) {
+            LOG(ERROR) << "EVP_EncryptUpdate: Unexpected outl=" << numWritten << " (expected "
+                       << data.size() << ")";
+            return {};
+        }
+    }
+
+    if (EVP_EncryptFinal_ex(ctx.get(), cipherText + numWritten, &numWritten) != 1) {
+        LOG(ERROR) << "EVP_EncryptFinal_ex: failed";
+        return {};
+    }
+    if (numWritten != 0) {
+        LOG(ERROR) << "EVP_EncryptFinal_ex: Unexpected non-zero outl=" << numWritten;
+        return {};
+    }
+
+    if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, kAesGcmTagSize, tag) != 1) {
+        LOG(ERROR) << "EVP_CIPHER_CTX_ctrl: failed getting tag";
+        return {};
+    }
+
+    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) {
+        ret.insert(ret.end(), certificate.begin(), certificate.end());
+    }
+    return ret;
+}
+
+optional<vector<vector<uint8_t>>> certificateChainSplit(const vector<uint8_t>& certificateChain) {
+    const unsigned char* pStart = (unsigned char*)certificateChain.data();
+    const unsigned char* p = pStart;
+    const unsigned char* pEnd = p + certificateChain.size();
+    vector<vector<uint8_t>> certificates;
+    while (p < pEnd) {
+        size_t begin = p - pStart;
+        auto x509 = X509_Ptr(d2i_X509(nullptr, &p, pEnd - p));
+        size_t next = p - pStart;
+        if (x509 == nullptr) {
+            LOG(ERROR) << "Error parsing X509 certificate";
+            return {};
+        }
+        vector<uint8_t> cert =
+                vector<uint8_t>(certificateChain.begin() + begin, certificateChain.begin() + next);
+        certificates.push_back(std::move(cert));
+    }
+    return certificates;
+}
+
+static bool parseX509Certificates(const vector<uint8_t>& certificateChain,
+                                  vector<X509_Ptr>& parsedCertificates) {
+    const unsigned char* p = (unsigned char*)certificateChain.data();
+    const unsigned char* pEnd = p + certificateChain.size();
+    parsedCertificates.resize(0);
+    while (p < pEnd) {
+        auto x509 = X509_Ptr(d2i_X509(nullptr, &p, pEnd - p));
+        if (x509 == nullptr) {
+            LOG(ERROR) << "Error parsing X509 certificate";
+            return false;
+        }
+        parsedCertificates.push_back(std::move(x509));
+    }
+    return true;
+}
+
+// TODO: Right now the only check we perform is to check that each certificate
+//       is signed by its successor. We should - but currently don't - also check
+//       things like valid dates etc.
+//
+//       It would be nice to use X509_verify_cert() instead of doing our own thing.
+//
+bool certificateChainValidate(const vector<uint8_t>& certificateChain) {
+    vector<X509_Ptr> certs;
+
+    if (!parseX509Certificates(certificateChain, certs)) {
+        LOG(ERROR) << "Error parsing X509 certificates";
+        return false;
+    }
+
+    if (certs.size() == 1) {
+        return true;
+    }
+
+    for (size_t n = 1; n < certs.size(); n++) {
+        const X509_Ptr& keyCert = certs[n - 1];
+        const X509_Ptr& signingCert = certs[n];
+        EVP_PKEY_Ptr signingPubkey(X509_get_pubkey(signingCert.get()));
+        if (X509_verify(keyCert.get(), signingPubkey.get()) != 1) {
+            LOG(ERROR) << "Error validating cert at index " << n - 1
+                       << " is signed by its successor";
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool checkEcDsaSignature(const vector<uint8_t>& digest, const vector<uint8_t>& signature,
+                         const vector<uint8_t>& publicKey) {
+    const unsigned char* p = (unsigned char*)signature.data();
+    auto sig = ECDSA_SIG_Ptr(d2i_ECDSA_SIG(nullptr, &p, signature.size()));
+    if (sig.get() == nullptr) {
+        LOG(ERROR) << "Error decoding DER encoded signature";
+        return false;
+    }
+
+    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) !=
+        1) {
+        LOG(ERROR) << "Error decoding publicKey";
+        return false;
+    }
+    auto ecKey = EC_KEY_Ptr(EC_KEY_new());
+    auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
+    if (ecKey.get() == nullptr || pkey.get() == nullptr) {
+        LOG(ERROR) << "Memory allocation failed";
+        return false;
+    }
+    if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) {
+        LOG(ERROR) << "Error setting group";
+        return false;
+    }
+    if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) {
+        LOG(ERROR) << "Error setting point";
+        return false;
+    }
+    if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) {
+        LOG(ERROR) << "Error setting key";
+        return false;
+    }
+
+    int rc = ECDSA_do_verify(digest.data(), digest.size(), sig.get(), ecKey.get());
+    if (rc != 1) {
+        LOG(ERROR) << "Error verifying signature (rc=" << rc << ")";
+        return false;
+    }
+
+    return true;
+}
+
+vector<uint8_t> sha256(const vector<uint8_t>& data) {
+    vector<uint8_t> ret;
+    ret.resize(SHA256_DIGEST_LENGTH);
+    SHA256_CTX ctx;
+    SHA256_Init(&ctx);
+    SHA256_Update(&ctx, data.data(), data.size());
+    SHA256_Final((unsigned char*)ret.data(), &ctx);
+    return ret;
+}
+
+optional<vector<uint8_t>> signEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data) {
+    auto bn = BIGNUM_Ptr(BN_bin2bn(key.data(), key.size(), nullptr));
+    if (bn.get() == nullptr) {
+        LOG(ERROR) << "Error creating BIGNUM";
+        return {};
+    }
+
+    auto ec_key = EC_KEY_Ptr(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+    if (EC_KEY_set_private_key(ec_key.get(), bn.get()) != 1) {
+        LOG(ERROR) << "Error setting private key from BIGNUM";
+        return {};
+    }
+
+    auto digest = sha256(data);
+    ECDSA_SIG* sig = ECDSA_do_sign(digest.data(), digest.size(), ec_key.get());
+    if (sig == nullptr) {
+        LOG(ERROR) << "Error signing digest";
+        return {};
+    }
+    size_t len = i2d_ECDSA_SIG(sig, nullptr);
+    vector<uint8_t> signature;
+    signature.resize(len);
+    unsigned char* p = (unsigned char*)signature.data();
+    i2d_ECDSA_SIG(sig, &p);
+    ECDSA_SIG_free(sig);
+    return signature;
+}
+
+optional<vector<uint8_t>> hmacSha256(const vector<uint8_t>& key, const vector<uint8_t>& data) {
+    HMAC_CTX ctx;
+    HMAC_CTX_init(&ctx);
+    if (HMAC_Init_ex(&ctx, key.data(), key.size(), EVP_sha256(), nullptr /* impl */) != 1) {
+        LOG(ERROR) << "Error initializing HMAC_CTX";
+        return {};
+    }
+    if (HMAC_Update(&ctx, data.data(), data.size()) != 1) {
+        LOG(ERROR) << "Error updating HMAC_CTX";
+        return {};
+    }
+    vector<uint8_t> hmac;
+    hmac.resize(32);
+    unsigned int size = 0;
+    if (HMAC_Final(&ctx, hmac.data(), &size) != 1) {
+        LOG(ERROR) << "Error finalizing HMAC_CTX";
+        return {};
+    }
+    if (size != 32) {
+        LOG(ERROR) << "Expected 32 bytes from HMAC_Final, got " << size;
+        return {};
+    }
+    return hmac;
+}
+
+optional<vector<uint8_t>> createEcKeyPair() {
+    auto ec_key = EC_KEY_Ptr(EC_KEY_new());
+    auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
+    auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+    if (ec_key.get() == nullptr || pkey.get() == nullptr) {
+        LOG(ERROR) << "Memory allocation failed";
+        return {};
+    }
+
+    if (EC_KEY_set_group(ec_key.get(), group.get()) != 1 ||
+        EC_KEY_generate_key(ec_key.get()) != 1 || EC_KEY_check_key(ec_key.get()) < 0) {
+        LOG(ERROR) << "Error generating key";
+        return {};
+    }
+
+    if (EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()) != 1) {
+        LOG(ERROR) << "Error getting private key";
+        return {};
+    }
+
+    int size = i2d_PrivateKey(pkey.get(), nullptr);
+    if (size == 0) {
+        LOG(ERROR) << "Error generating public key encoding";
+        return {};
+    }
+    vector<uint8_t> keyPair;
+    keyPair.resize(size);
+    unsigned char* p = keyPair.data();
+    i2d_PrivateKey(pkey.get(), &p);
+    return keyPair;
+}
+
+optional<vector<uint8_t>> ecKeyPairGetPublicKey(const vector<uint8_t>& keyPair) {
+    const unsigned char* p = (const unsigned char*)keyPair.data();
+    auto pkey = EVP_PKEY_Ptr(d2i_PrivateKey(EVP_PKEY_EC, nullptr, &p, keyPair.size()));
+    if (pkey.get() == nullptr) {
+        LOG(ERROR) << "Error parsing keyPair";
+        return {};
+    }
+
+    auto ecKey = EC_KEY_Ptr(EVP_PKEY_get1_EC_KEY(pkey.get()));
+    if (ecKey.get() == nullptr) {
+        LOG(ERROR) << "Failed getting EC key";
+        return {};
+    }
+
+    auto ecGroup = EC_KEY_get0_group(ecKey.get());
+    auto ecPoint = EC_KEY_get0_public_key(ecKey.get());
+    int size = EC_POINT_point2oct(ecGroup, ecPoint, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0,
+                                  nullptr);
+    if (size == 0) {
+        LOG(ERROR) << "Error generating public key encoding";
+        return {};
+    }
+
+    vector<uint8_t> publicKey;
+    publicKey.resize(size);
+    EC_POINT_point2oct(ecGroup, ecPoint, POINT_CONVERSION_UNCOMPRESSED, publicKey.data(),
+                       publicKey.size(), nullptr);
+    return publicKey;
+}
+
+optional<vector<uint8_t>> ecKeyPairGetPrivateKey(const vector<uint8_t>& keyPair) {
+    const unsigned char* p = (const unsigned char*)keyPair.data();
+    auto pkey = EVP_PKEY_Ptr(d2i_PrivateKey(EVP_PKEY_EC, nullptr, &p, keyPair.size()));
+    if (pkey.get() == nullptr) {
+        LOG(ERROR) << "Error parsing keyPair";
+        return {};
+    }
+
+    auto ecKey = EC_KEY_Ptr(EVP_PKEY_get1_EC_KEY(pkey.get()));
+    if (ecKey.get() == nullptr) {
+        LOG(ERROR) << "Failed getting EC key";
+        return {};
+    }
+
+    const BIGNUM* bignum = EC_KEY_get0_private_key(ecKey.get());
+    if (bignum == nullptr) {
+        LOG(ERROR) << "Error getting bignum from private key";
+        return {};
+    }
+    vector<uint8_t> privateKey;
+    privateKey.resize(BN_num_bytes(bignum));
+    BN_bn2bin(bignum, privateKey.data());
+    return privateKey;
+}
+
+optional<vector<uint8_t>> ecKeyPairGetPkcs12(const vector<uint8_t>& keyPair, const string& name,
+                                             const string& serialDecimal, const string& issuer,
+                                             const string& subject, time_t validityNotBefore,
+                                             time_t validityNotAfter) {
+    const unsigned char* p = (const unsigned char*)keyPair.data();
+    auto pkey = EVP_PKEY_Ptr(d2i_PrivateKey(EVP_PKEY_EC, nullptr, &p, keyPair.size()));
+    if (pkey.get() == nullptr) {
+        LOG(ERROR) << "Error parsing keyPair";
+        return {};
+    }
+
+    auto x509 = X509_Ptr(X509_new());
+    if (!x509.get()) {
+        LOG(ERROR) << "Error creating X509 certificate";
+        return {};
+    }
+
+    if (!X509_set_version(x509.get(), 2 /* version 3, but zero-based */)) {
+        LOG(ERROR) << "Error setting version to 3";
+        return {};
+    }
+
+    if (X509_set_pubkey(x509.get(), pkey.get()) != 1) {
+        LOG(ERROR) << "Error setting public key";
+        return {};
+    }
+
+    BIGNUM* bignumSerial = nullptr;
+    if (BN_dec2bn(&bignumSerial, serialDecimal.c_str()) == 0) {
+        LOG(ERROR) << "Error parsing serial";
+        return {};
+    }
+    auto bignumSerialPtr = BIGNUM_Ptr(bignumSerial);
+    auto asnSerial = ASN1_INTEGER_Ptr(BN_to_ASN1_INTEGER(bignumSerial, nullptr));
+    if (X509_set_serialNumber(x509.get(), asnSerial.get()) != 1) {
+        LOG(ERROR) << "Error setting serial";
+        return {};
+    }
+
+    auto x509Issuer = X509_NAME_Ptr(X509_NAME_new());
+    if (x509Issuer.get() == nullptr ||
+        X509_NAME_add_entry_by_txt(x509Issuer.get(), "CN", MBSTRING_ASC,
+                                   (const uint8_t*)issuer.c_str(), issuer.size(), -1 /* loc */,
+                                   0 /* set */) != 1 ||
+        X509_set_issuer_name(x509.get(), x509Issuer.get()) != 1) {
+        LOG(ERROR) << "Error setting issuer";
+        return {};
+    }
+
+    auto x509Subject = X509_NAME_Ptr(X509_NAME_new());
+    if (x509Subject.get() == nullptr ||
+        X509_NAME_add_entry_by_txt(x509Subject.get(), "CN", MBSTRING_ASC,
+                                   (const uint8_t*)subject.c_str(), subject.size(), -1 /* loc */,
+                                   0 /* set */) != 1 ||
+        X509_set_subject_name(x509.get(), x509Subject.get()) != 1) {
+        LOG(ERROR) << "Error setting subject";
+        return {};
+    }
+
+    auto asnNotBefore = ASN1_TIME_Ptr(ASN1_TIME_set(nullptr, validityNotBefore));
+    if (asnNotBefore.get() == nullptr || X509_set_notBefore(x509.get(), asnNotBefore.get()) != 1) {
+        LOG(ERROR) << "Error setting notBefore";
+        return {};
+    }
+
+    auto asnNotAfter = ASN1_TIME_Ptr(ASN1_TIME_set(nullptr, validityNotAfter));
+    if (asnNotAfter.get() == nullptr || X509_set_notAfter(x509.get(), asnNotAfter.get()) != 1) {
+        LOG(ERROR) << "Error setting notAfter";
+        return {};
+    }
+
+    if (X509_sign(x509.get(), pkey.get(), EVP_sha256()) == 0) {
+        LOG(ERROR) << "Error signing X509 certificate";
+        return {};
+    }
+
+    // Ideally we wouldn't encrypt it (we're only using this function for
+    // sending a key-pair over binder to the Android app) but BoringSSL does not
+    // support this: from pkcs8_x509.c in BoringSSL: "In OpenSSL, -1 here means
+    // to use no encryption, which we do not currently support."
+    //
+    // Passing nullptr as |pass|, though, means "no password". So we'll do that.
+    // Compare with the receiving side - CredstoreIdentityCredential.java - where
+    // an empty char[] is passed as the password.
+    //
+    auto pkcs12 = PKCS12_Ptr(PKCS12_create(nullptr, name.c_str(), pkey.get(), x509.get(),
+                                           nullptr,  // ca
+                                           0,        // nid_key
+                                           0,        // nid_cert
+                                           0,        // iter,
+                                           0,        // mac_iter,
+                                           0));      // keytype
+    if (pkcs12.get() == nullptr) {
+        char buf[128];
+        long errCode = ERR_get_error();
+        ERR_error_string_n(errCode, buf, sizeof buf);
+        LOG(ERROR) << "Error creating PKCS12, code " << errCode << ": " << buf;
+        return {};
+    }
+
+    unsigned char* buffer = nullptr;
+    int length = i2d_PKCS12(pkcs12.get(), &buffer);
+    if (length < 0) {
+        LOG(ERROR) << "Error encoding PKCS12";
+        return {};
+    }
+    vector<uint8_t> pkcs12Bytes;
+    pkcs12Bytes.resize(length);
+    memcpy(pkcs12Bytes.data(), buffer, length);
+    OPENSSL_free(buffer);
+
+    return pkcs12Bytes;
+}
+
+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) {
+    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) !=
+        1) {
+        LOG(ERROR) << "Error decoding publicKey";
+        return {};
+    }
+    auto ecKey = EC_KEY_Ptr(EC_KEY_new());
+    auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
+    if (ecKey.get() == nullptr || pkey.get() == nullptr) {
+        LOG(ERROR) << "Memory allocation failed";
+        return {};
+    }
+    if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) {
+        LOG(ERROR) << "Error setting group";
+        return {};
+    }
+    if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) {
+        LOG(ERROR) << "Error setting point";
+        return {};
+    }
+    if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) {
+        LOG(ERROR) << "Error setting key";
+        return {};
+    }
+
+    auto bn = BIGNUM_Ptr(BN_bin2bn(signingKey.data(), signingKey.size(), nullptr));
+    if (bn.get() == nullptr) {
+        LOG(ERROR) << "Error creating BIGNUM for private key";
+        return {};
+    }
+    auto privEcKey = EC_KEY_Ptr(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+    if (EC_KEY_set_private_key(privEcKey.get(), bn.get()) != 1) {
+        LOG(ERROR) << "Error setting private key from BIGNUM";
+        return {};
+    }
+    auto privPkey = EVP_PKEY_Ptr(EVP_PKEY_new());
+    if (EVP_PKEY_set1_EC_KEY(privPkey.get(), privEcKey.get()) != 1) {
+        LOG(ERROR) << "Error setting private key";
+        return {};
+    }
+
+    auto x509 = X509_Ptr(X509_new());
+    if (!x509.get()) {
+        LOG(ERROR) << "Error creating X509 certificate";
+        return {};
+    }
+
+    if (!X509_set_version(x509.get(), 2 /* version 3, but zero-based */)) {
+        LOG(ERROR) << "Error setting version to 3";
+        return {};
+    }
+
+    if (X509_set_pubkey(x509.get(), pkey.get()) != 1) {
+        LOG(ERROR) << "Error setting public key";
+        return {};
+    }
+
+    BIGNUM* bignumSerial = nullptr;
+    if (BN_dec2bn(&bignumSerial, serialDecimal.c_str()) == 0) {
+        LOG(ERROR) << "Error parsing serial";
+        return {};
+    }
+    auto bignumSerialPtr = BIGNUM_Ptr(bignumSerial);
+    auto asnSerial = ASN1_INTEGER_Ptr(BN_to_ASN1_INTEGER(bignumSerial, nullptr));
+    if (X509_set_serialNumber(x509.get(), asnSerial.get()) != 1) {
+        LOG(ERROR) << "Error setting serial";
+        return {};
+    }
+
+    auto x509Issuer = X509_NAME_Ptr(X509_NAME_new());
+    if (x509Issuer.get() == nullptr ||
+        X509_NAME_add_entry_by_txt(x509Issuer.get(), "CN", MBSTRING_ASC,
+                                   (const uint8_t*)issuer.c_str(), issuer.size(), -1 /* loc */,
+                                   0 /* set */) != 1 ||
+        X509_set_issuer_name(x509.get(), x509Issuer.get()) != 1) {
+        LOG(ERROR) << "Error setting issuer";
+        return {};
+    }
+
+    auto x509Subject = X509_NAME_Ptr(X509_NAME_new());
+    if (x509Subject.get() == nullptr ||
+        X509_NAME_add_entry_by_txt(x509Subject.get(), "CN", MBSTRING_ASC,
+                                   (const uint8_t*)subject.c_str(), subject.size(), -1 /* loc */,
+                                   0 /* set */) != 1 ||
+        X509_set_subject_name(x509.get(), x509Subject.get()) != 1) {
+        LOG(ERROR) << "Error setting subject";
+        return {};
+    }
+
+    auto asnNotBefore = ASN1_TIME_Ptr(ASN1_TIME_set(nullptr, validityNotBefore));
+    if (asnNotBefore.get() == nullptr || X509_set_notBefore(x509.get(), asnNotBefore.get()) != 1) {
+        LOG(ERROR) << "Error setting notBefore";
+        return {};
+    }
+
+    auto asnNotAfter = ASN1_TIME_Ptr(ASN1_TIME_set(nullptr, validityNotAfter));
+    if (asnNotAfter.get() == nullptr || X509_set_notAfter(x509.get(), asnNotAfter.get()) != 1) {
+        LOG(ERROR) << "Error setting notAfter";
+        return {};
+    }
+
+    if (X509_sign(x509.get(), privPkey.get(), EVP_sha256()) == 0) {
+        LOG(ERROR) << "Error signing X509 certificate";
+        return {};
+    }
+
+    unsigned char* buffer = nullptr;
+    int length = i2d_X509(x509.get(), &buffer);
+    if (length < 0) {
+        LOG(ERROR) << "Error DER encoding X509 certificate";
+        return {};
+    }
+
+    vector<uint8_t> certificate;
+    certificate.resize(length);
+    memcpy(certificate.data(), buffer, length);
+    OPENSSL_free(buffer);
+    return certificate;
+}
+
+optional<vector<uint8_t>> ecdh(const vector<uint8_t>& publicKey,
+                               const vector<uint8_t>& privateKey) {
+    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) !=
+        1) {
+        LOG(ERROR) << "Error decoding publicKey";
+        return {};
+    }
+    auto ecKey = EC_KEY_Ptr(EC_KEY_new());
+    auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
+    if (ecKey.get() == nullptr || pkey.get() == nullptr) {
+        LOG(ERROR) << "Memory allocation failed";
+        return {};
+    }
+    if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) {
+        LOG(ERROR) << "Error setting group";
+        return {};
+    }
+    if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) {
+        LOG(ERROR) << "Error setting point";
+        return {};
+    }
+    if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) {
+        LOG(ERROR) << "Error setting key";
+        return {};
+    }
+
+    auto bn = BIGNUM_Ptr(BN_bin2bn(privateKey.data(), privateKey.size(), nullptr));
+    if (bn.get() == nullptr) {
+        LOG(ERROR) << "Error creating BIGNUM for private key";
+        return {};
+    }
+    auto privEcKey = EC_KEY_Ptr(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+    if (EC_KEY_set_private_key(privEcKey.get(), bn.get()) != 1) {
+        LOG(ERROR) << "Error setting private key from BIGNUM";
+        return {};
+    }
+    auto privPkey = EVP_PKEY_Ptr(EVP_PKEY_new());
+    if (EVP_PKEY_set1_EC_KEY(privPkey.get(), privEcKey.get()) != 1) {
+        LOG(ERROR) << "Error setting private key";
+        return {};
+    }
+
+    auto ctx = EVP_PKEY_CTX_Ptr(EVP_PKEY_CTX_new(privPkey.get(), NULL));
+    if (ctx.get() == nullptr) {
+        LOG(ERROR) << "Error creating context";
+        return {};
+    }
+
+    if (EVP_PKEY_derive_init(ctx.get()) != 1) {
+        LOG(ERROR) << "Error initializing context";
+        return {};
+    }
+
+    if (EVP_PKEY_derive_set_peer(ctx.get(), pkey.get()) != 1) {
+        LOG(ERROR) << "Error setting peer";
+        return {};
+    }
+
+    /* Determine buffer length for shared secret */
+    size_t secretLen = 0;
+    if (EVP_PKEY_derive(ctx.get(), NULL, &secretLen) != 1) {
+        LOG(ERROR) << "Error determing length of shared secret";
+        return {};
+    }
+    vector<uint8_t> sharedSecret;
+    sharedSecret.resize(secretLen);
+
+    if (EVP_PKEY_derive(ctx.get(), sharedSecret.data(), &secretLen) != 1) {
+        LOG(ERROR) << "Error deriving shared secret";
+        return {};
+    }
+    return sharedSecret;
+}
+
+optional<vector<uint8_t>> hkdf(const vector<uint8_t>& sharedSecret, const vector<uint8_t>& salt,
+                               const vector<uint8_t>& info, size_t size) {
+    vector<uint8_t> derivedKey;
+    derivedKey.resize(size);
+    if (HKDF(derivedKey.data(), derivedKey.size(), EVP_sha256(), sharedSecret.data(),
+             sharedSecret.size(), salt.data(), salt.size(), info.data(), info.size()) != 1) {
+        LOG(ERROR) << "Error deriving key";
+        return {};
+    }
+    return derivedKey;
+}
+
+void removeLeadingZeroes(vector<uint8_t>& vec) {
+    while (vec.size() >= 1 && vec[0] == 0x00) {
+        vec.erase(vec.begin());
+    }
+}
+
+tuple<bool, vector<uint8_t>, vector<uint8_t>> ecPublicKeyGetXandY(
+        const vector<uint8_t>& publicKey) {
+    if (publicKey.size() != 65 || publicKey[0] != 0x04) {
+        LOG(ERROR) << "publicKey is not in the expected format";
+        return std::make_tuple(false, vector<uint8_t>(), vector<uint8_t>());
+    }
+    vector<uint8_t> x, y;
+    x.resize(32);
+    y.resize(32);
+    memcpy(x.data(), publicKey.data() + 1, 32);
+    memcpy(y.data(), publicKey.data() + 33, 32);
+
+    removeLeadingZeroes(x);
+    removeLeadingZeroes(y);
+
+    return std::make_tuple(true, x, y);
+}
+
+optional<vector<uint8_t>> certificateChainGetTopMostKey(const vector<uint8_t>& certificateChain) {
+    vector<X509_Ptr> certs;
+    if (!parseX509Certificates(certificateChain, certs)) {
+        return {};
+    }
+    if (certs.size() < 1) {
+        LOG(ERROR) << "No certificates in chain";
+        return {};
+    }
+
+    int algoId = OBJ_obj2nid(certs[0]->cert_info->key->algor->algorithm);
+    if (algoId != NID_X9_62_id_ecPublicKey) {
+        LOG(ERROR) << "Expected NID_X9_62_id_ecPublicKey, got " << OBJ_nid2ln(algoId);
+        return {};
+    }
+
+    auto pkey = EVP_PKEY_Ptr(X509_get_pubkey(certs[0].get()));
+    if (pkey.get() == nullptr) {
+        LOG(ERROR) << "No public key";
+        return {};
+    }
+
+    auto ecKey = EC_KEY_Ptr(EVP_PKEY_get1_EC_KEY(pkey.get()));
+    if (ecKey.get() == nullptr) {
+        LOG(ERROR) << "Failed getting EC key";
+        return {};
+    }
+
+    auto ecGroup = EC_KEY_get0_group(ecKey.get());
+    auto ecPoint = EC_KEY_get0_public_key(ecKey.get());
+    int size = EC_POINT_point2oct(ecGroup, ecPoint, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0,
+                                  nullptr);
+    if (size == 0) {
+        LOG(ERROR) << "Error generating public key encoding";
+        return {};
+    }
+    vector<uint8_t> publicKey;
+    publicKey.resize(size);
+    EC_POINT_point2oct(ecGroup, ecPoint, POINT_CONVERSION_UNCOMPRESSED, publicKey.data(),
+                       publicKey.size(), nullptr);
+    return publicKey;
+}
+
+// ---------------------------------------------------------------------------
+// COSE Utility Functions
+// ---------------------------------------------------------------------------
+
+vector<uint8_t> coseBuildToBeSigned(const vector<uint8_t>& encodedProtectedHeaders,
+                                    const vector<uint8_t>& data,
+                                    const vector<uint8_t>& detachedContent) {
+    cppbor::Array sigStructure;
+    sigStructure.add("Signature1");
+    sigStructure.add(encodedProtectedHeaders);
+
+    // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+    // so external_aad is the empty bstr
+    vector<uint8_t> emptyExternalAad;
+    sigStructure.add(emptyExternalAad);
+
+    // Next field is the payload, independently of how it's transported (RFC
+    // 8152 section 4.4). Since our API specifies only one of |data| and
+    // |detachedContent| can be non-empty, it's simply just the non-empty one.
+    if (data.size() > 0) {
+        sigStructure.add(data);
+    } else {
+        sigStructure.add(detachedContent);
+    }
+    return sigStructure.encode();
+}
+
+vector<uint8_t> coseEncodeHeaders(const cppbor::Map& protectedHeaders) {
+    if (protectedHeaders.size() == 0) {
+        cppbor::Bstr emptyBstr(vector<uint8_t>({}));
+        return emptyBstr.encode();
+    }
+    return protectedHeaders.encode();
+}
+
+// From https://tools.ietf.org/html/rfc8152
+const int COSE_LABEL_ALG = 1;
+const int COSE_LABEL_X5CHAIN = 33;  // temporary identifier
+
+// From "COSE Algorithms" registry
+const int COSE_ALG_ECDSA_256 = -7;
+const int COSE_ALG_HMAC_256_256 = 5;
+
+bool ecdsaSignatureCoseToDer(const vector<uint8_t>& ecdsaCoseSignature,
+                             vector<uint8_t>& ecdsaDerSignature) {
+    if (ecdsaCoseSignature.size() != 64) {
+        LOG(ERROR) << "COSE signature length is " << ecdsaCoseSignature.size() << ", expected 64";
+        return false;
+    }
+
+    auto rBn = BIGNUM_Ptr(BN_bin2bn(ecdsaCoseSignature.data(), 32, nullptr));
+    if (rBn.get() == nullptr) {
+        LOG(ERROR) << "Error creating BIGNUM for r";
+        return false;
+    }
+
+    auto sBn = BIGNUM_Ptr(BN_bin2bn(ecdsaCoseSignature.data() + 32, 32, nullptr));
+    if (sBn.get() == nullptr) {
+        LOG(ERROR) << "Error creating BIGNUM for s";
+        return false;
+    }
+
+    ECDSA_SIG sig;
+    sig.r = rBn.get();
+    sig.s = sBn.get();
+
+    size_t len = i2d_ECDSA_SIG(&sig, nullptr);
+    ecdsaDerSignature.resize(len);
+    unsigned char* p = (unsigned char*)ecdsaDerSignature.data();
+    i2d_ECDSA_SIG(&sig, &p);
+
+    return true;
+}
+
+bool ecdsaSignatureDerToCose(const vector<uint8_t>& ecdsaDerSignature,
+                             vector<uint8_t>& ecdsaCoseSignature) {
+    ECDSA_SIG* sig;
+    const unsigned char* p = ecdsaDerSignature.data();
+    sig = d2i_ECDSA_SIG(nullptr, &p, ecdsaDerSignature.size());
+    if (sig == nullptr) {
+        LOG(ERROR) << "Error decoding DER signature";
+        return false;
+    }
+
+    ecdsaCoseSignature.clear();
+    ecdsaCoseSignature.resize(64);
+    if (BN_bn2binpad(sig->r, ecdsaCoseSignature.data(), 32) != 32) {
+        LOG(ERROR) << "Error encoding r";
+        return false;
+    }
+    if (BN_bn2binpad(sig->s, ecdsaCoseSignature.data() + 32, 32) != 32) {
+        LOG(ERROR) << "Error encoding s";
+        return false;
+    }
+    return true;
+}
+
+optional<vector<uint8_t>> coseSignEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data,
+                                        const vector<uint8_t>& detachedContent,
+                                        const vector<uint8_t>& certificateChain) {
+    cppbor::Map unprotectedHeaders;
+    cppbor::Map protectedHeaders;
+
+    if (data.size() > 0 && detachedContent.size() > 0) {
+        LOG(ERROR) << "data and detachedContent cannot both be non-empty";
+        return {};
+    }
+
+    protectedHeaders.add(COSE_LABEL_ALG, COSE_ALG_ECDSA_256);
+
+    if (certificateChain.size() != 0) {
+        optional<vector<vector<uint8_t>>> certs = support::certificateChainSplit(certificateChain);
+        if (!certs) {
+            LOG(ERROR) << "Error splitting certificate chain";
+            return {};
+        }
+        if (certs.value().size() == 1) {
+            unprotectedHeaders.add(COSE_LABEL_X5CHAIN, certs.value()[0]);
+        } else {
+            cppbor::Array certArray;
+            for (const vector<uint8_t>& cert : certs.value()) {
+                certArray.add(cert);
+            }
+            unprotectedHeaders.add(COSE_LABEL_X5CHAIN, std::move(certArray));
+        }
+    }
+
+    vector<uint8_t> encodedProtectedHeaders = coseEncodeHeaders(protectedHeaders);
+    vector<uint8_t> toBeSigned =
+            coseBuildToBeSigned(encodedProtectedHeaders, data, detachedContent);
+
+    optional<vector<uint8_t>> derSignature = signEcDsa(key, toBeSigned);
+    if (!derSignature) {
+        LOG(ERROR) << "Error signing toBeSigned data";
+        return {};
+    }
+    vector<uint8_t> coseSignature;
+    if (!ecdsaSignatureDerToCose(derSignature.value(), coseSignature)) {
+        LOG(ERROR) << "Error converting ECDSA signature from DER to COSE format";
+        return {};
+    }
+
+    cppbor::Array coseSign1;
+    coseSign1.add(encodedProtectedHeaders);
+    coseSign1.add(std::move(unprotectedHeaders));
+    if (data.size() == 0) {
+        cppbor::Null nullValue;
+        coseSign1.add(std::move(nullValue));
+    } else {
+        coseSign1.add(data);
+    }
+    coseSign1.add(coseSignature);
+    vector<uint8_t> signatureCoseSign1;
+    signatureCoseSign1 = coseSign1.encode();
+    return signatureCoseSign1;
+}
+
+bool coseCheckEcDsaSignature(const vector<uint8_t>& signatureCoseSign1,
+                             const vector<uint8_t>& detachedContent,
+                             const vector<uint8_t>& publicKey) {
+    auto [item, _, message] = cppbor::parse(signatureCoseSign1);
+    if (item == nullptr) {
+        LOG(ERROR) << "Passed-in COSE_Sign1 is not valid CBOR: " << message;
+        return false;
+    }
+    const cppbor::Array* array = item->asArray();
+    if (array == nullptr) {
+        LOG(ERROR) << "Value for COSE_Sign1 is not an array";
+        return false;
+    }
+    if (array->size() != 4) {
+        LOG(ERROR) << "Value for COSE_Sign1 is not an array of size 4";
+        return false;
+    }
+
+    const cppbor::Bstr* encodedProtectedHeadersBstr = (*array)[0]->asBstr();
+    ;
+    if (encodedProtectedHeadersBstr == nullptr) {
+        LOG(ERROR) << "Value for encodedProtectedHeaders is not a bstr";
+        return false;
+    }
+    const vector<uint8_t> encodedProtectedHeaders = encodedProtectedHeadersBstr->value();
+
+    const cppbor::Map* unprotectedHeaders = (*array)[1]->asMap();
+    if (unprotectedHeaders == nullptr) {
+        LOG(ERROR) << "Value for unprotectedHeaders is not a map";
+        return false;
+    }
+
+    vector<uint8_t> data;
+    const cppbor::Simple* payloadAsSimple = (*array)[2]->asSimple();
+    if (payloadAsSimple != nullptr) {
+        if (payloadAsSimple->asNull() == nullptr) {
+            LOG(ERROR) << "Value for payload is not null or a bstr";
+            return false;
+        }
+    } else {
+        const cppbor::Bstr* payloadAsBstr = (*array)[2]->asBstr();
+        if (payloadAsBstr == nullptr) {
+            LOG(ERROR) << "Value for payload is not null or a bstr";
+            return false;
+        }
+        data = payloadAsBstr->value();  // TODO: avoid copy
+    }
+
+    if (data.size() > 0 && detachedContent.size() > 0) {
+        LOG(ERROR) << "data and detachedContent cannot both be non-empty";
+        return false;
+    }
+
+    const cppbor::Bstr* signatureBstr = (*array)[3]->asBstr();
+    if (signatureBstr == nullptr) {
+        LOG(ERROR) << "Value for signature is a bstr";
+        return false;
+    }
+    const vector<uint8_t>& coseSignature = signatureBstr->value();
+
+    vector<uint8_t> derSignature;
+    if (!ecdsaSignatureCoseToDer(coseSignature, derSignature)) {
+        LOG(ERROR) << "Error converting ECDSA signature from COSE to DER format";
+        return false;
+    }
+
+    vector<uint8_t> toBeSigned =
+            coseBuildToBeSigned(encodedProtectedHeaders, data, detachedContent);
+    if (!checkEcDsaSignature(support::sha256(toBeSigned), derSignature, publicKey)) {
+        LOG(ERROR) << "Signature check failed";
+        return false;
+    }
+    return true;
+}
+
+optional<vector<uint8_t>> coseSignGetPayload(const vector<uint8_t>& signatureCoseSign1) {
+    auto [item, _, message] = cppbor::parse(signatureCoseSign1);
+    if (item == nullptr) {
+        LOG(ERROR) << "Passed-in COSE_Sign1 is not valid CBOR: " << message;
+        return {};
+    }
+    const cppbor::Array* array = item->asArray();
+    if (array == nullptr) {
+        LOG(ERROR) << "Value for COSE_Sign1 is not an array";
+        return {};
+    }
+    if (array->size() != 4) {
+        LOG(ERROR) << "Value for COSE_Sign1 is not an array of size 4";
+        return {};
+    }
+
+    vector<uint8_t> data;
+    const cppbor::Simple* payloadAsSimple = (*array)[2]->asSimple();
+    if (payloadAsSimple != nullptr) {
+        if (payloadAsSimple->asNull() == nullptr) {
+            LOG(ERROR) << "Value for payload is not null or a bstr";
+            return {};
+        }
+        // payload is null, so |data| should be empty (as it is)
+    } else {
+        const cppbor::Bstr* payloadAsBstr = (*array)[2]->asBstr();
+        if (payloadAsBstr == nullptr) {
+            LOG(ERROR) << "Value for payload is not null or a bstr";
+            return {};
+        }
+        // Copy payload into |data|
+        data = payloadAsBstr->value();
+    }
+
+    return data;
+}
+
+optional<vector<uint8_t>> coseSignGetX5Chain(const vector<uint8_t>& signatureCoseSign1) {
+    auto [item, _, message] = cppbor::parse(signatureCoseSign1);
+    if (item == nullptr) {
+        LOG(ERROR) << "Passed-in COSE_Sign1 is not valid CBOR: " << message;
+        return {};
+    }
+    const cppbor::Array* array = item->asArray();
+    if (array == nullptr) {
+        LOG(ERROR) << "Value for COSE_Sign1 is not an array";
+        return {};
+    }
+    if (array->size() != 4) {
+        LOG(ERROR) << "Value for COSE_Sign1 is not an array of size 4";
+        return {};
+    }
+
+    const cppbor::Map* unprotectedHeaders = (*array)[1]->asMap();
+    if (unprotectedHeaders == nullptr) {
+        LOG(ERROR) << "Value for unprotectedHeaders is not a map";
+        return {};
+    }
+
+    for (size_t n = 0; n < unprotectedHeaders->size(); 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";
+            return {};
+        }
+        int label = number->value();
+        if (label == COSE_LABEL_X5CHAIN) {
+            const cppbor::Bstr* bstr = valueItem->asBstr();
+            if (bstr != nullptr) {
+                return bstr->value();
+            }
+            const cppbor::Array* array = valueItem->asArray();
+            if (array != nullptr) {
+                vector<uint8_t> certs;
+                for (size_t m = 0; m < array->size(); m++) {
+                    const cppbor::Bstr* bstr = ((*array)[m])->asBstr();
+                    if (bstr == nullptr) {
+                        LOG(ERROR) << "Item in x5chain array is not a bstr";
+                        return {};
+                    }
+                    const vector<uint8_t>& certValue = bstr->value();
+                    certs.insert(certs.end(), certValue.begin(), certValue.end());
+                }
+                return certs;
+            }
+            LOG(ERROR) << "Value for x5chain label is not a bstr or array";
+            return {};
+        }
+    }
+    LOG(ERROR) << "Did not find x5chain label in unprotected headers";
+    return {};
+}
+
+vector<uint8_t> coseBuildToBeMACed(const vector<uint8_t>& encodedProtectedHeaders,
+                                   const vector<uint8_t>& data,
+                                   const vector<uint8_t>& detachedContent) {
+    cppbor::Array macStructure;
+    macStructure.add("MAC0");
+    macStructure.add(encodedProtectedHeaders);
+
+    // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+    // so external_aad is the empty bstr
+    vector<uint8_t> emptyExternalAad;
+    macStructure.add(emptyExternalAad);
+
+    // Next field is the payload, independently of how it's transported (RFC
+    // 8152 section 4.4). Since our API specifies only one of |data| and
+    // |detachedContent| can be non-empty, it's simply just the non-empty one.
+    if (data.size() > 0) {
+        macStructure.add(data);
+    } else {
+        macStructure.add(detachedContent);
+    }
+
+    return macStructure.encode();
+}
+
+optional<vector<uint8_t>> coseMac0(const vector<uint8_t>& key, const vector<uint8_t>& data,
+                                   const vector<uint8_t>& detachedContent) {
+    cppbor::Map unprotectedHeaders;
+    cppbor::Map protectedHeaders;
+
+    if (data.size() > 0 && detachedContent.size() > 0) {
+        LOG(ERROR) << "data and detachedContent cannot both be non-empty";
+        return {};
+    }
+
+    protectedHeaders.add(COSE_LABEL_ALG, COSE_ALG_HMAC_256_256);
+
+    vector<uint8_t> encodedProtectedHeaders = coseEncodeHeaders(protectedHeaders);
+    vector<uint8_t> toBeMACed = coseBuildToBeMACed(encodedProtectedHeaders, data, detachedContent);
+
+    optional<vector<uint8_t>> mac = hmacSha256(key, toBeMACed);
+    if (!mac) {
+        LOG(ERROR) << "Error MACing toBeMACed data";
+        return {};
+    }
+
+    cppbor::Array array;
+    array.add(encodedProtectedHeaders);
+    array.add(std::move(unprotectedHeaders));
+    if (data.size() == 0) {
+        cppbor::Null nullValue;
+        array.add(std::move(nullValue));
+    } else {
+        array.add(data);
+    }
+    array.add(mac.value());
+    return array.encode();
+}
+
+// ---------------------------------------------------------------------------
+// Platform abstraction.
+// ---------------------------------------------------------------------------
+
+// This is not a very random HBK but that's OK because this is the SW
+// implementation where it can't be kept secret.
+vector<uint8_t> hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+
+const vector<uint8_t>& getHardwareBoundKey() {
+    return hardwareBoundKey;
+}
+
+// ---------------------------------------------------------------------------
+// Utility functions specific to IdentityCredential.
+// ---------------------------------------------------------------------------
+
+Result okResult{ResultCode::OK, ""};
+
+const Result& resultOK() {
+    return okResult;
+}
+
+Result result(ResultCode code, const char* format, ...) {
+    va_list ap;
+    va_start(ap, format);
+    string str;
+    android::base::StringAppendV(&str, format, ap);
+    va_end(ap);
+    return Result{code, str};
+}
+
+vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxChunkSize) {
+    vector<vector<uint8_t>> ret;
+
+    size_t contentSize = content.size();
+    if (contentSize <= maxChunkSize) {
+        ret.push_back(content);
+        return ret;
+    }
+
+    size_t numChunks = (contentSize + maxChunkSize - 1) / maxChunkSize;
+
+    size_t pos = 0;
+    for (size_t n = 0; n < numChunks; n++) {
+        size_t size = contentSize - pos;
+        if (size > maxChunkSize) {
+            size = maxChunkSize;
+        }
+        auto begin = content.begin() + pos;
+        auto end = content.begin() + pos + size;
+        ret.emplace_back(vector<uint8_t>(begin, end));
+        pos += maxChunkSize;
+    }
+
+    return ret;
+}
+
+vector<uint8_t> secureAccessControlProfileEncodeCbor(const SecureAccessControlProfile& profile) {
+    cppbor::Map map;
+    map.add("id", profile.id);
+
+    if (profile.readerCertificate.size() > 0) {
+        map.add("readerCertificate", cppbor::Bstr(profile.readerCertificate));
+    }
+
+    if (profile.userAuthenticationRequired) {
+        map.add("userAuthenticationRequired", profile.userAuthenticationRequired);
+        map.add("timeoutMillis", profile.timeoutMillis);
+        map.add("secureUserId", profile.secureUserId);
+    }
+
+    return map.encode();
+}
+
+optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
+        const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey) {
+    vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
+
+    optional<vector<uint8_t>> nonce = getRandom(12);
+    if (!nonce) {
+        return {};
+    }
+    optional<vector<uint8_t>> macO = encryptAes128Gcm(storageKey, nonce.value(), {}, cborData);
+    if (!macO) {
+        return {};
+    }
+    return macO.value();
+}
+
+bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
+                                        const vector<uint8_t>& storageKey) {
+    vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
+
+    if (profile.mac.size() < kAesGcmIvSize) {
+        return false;
+    }
+    vector<uint8_t> nonce =
+            vector<uint8_t>(profile.mac.begin(), profile.mac.begin() + kAesGcmIvSize);
+    optional<vector<uint8_t>> mac = encryptAes128Gcm(storageKey, nonce, {}, cborData);
+    if (!mac) {
+        return false;
+    }
+    if (mac.value() != vector<uint8_t>(profile.mac)) {
+        return false;
+    }
+    return true;
+}
+
+vector<uint8_t> testHardwareBoundKey = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+const vector<uint8_t>& getTestHardwareBoundKey() {
+    return testHardwareBoundKey;
+}
+
+vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
+                                          const vector<uint16_t> accessControlProfileIds) {
+    cppbor::Map map;
+    map.add("Namespace", nameSpace);
+    map.add("Name", name);
+
+    cppbor::Array acpIds;
+    for (auto id : accessControlProfileIds) {
+        acpIds.add(id);
+    }
+    map.add("AccessControlProfileIds", std::move(acpIds));
+    return map.encode();
+}
+
+}  // namespace support
+}  // namespace identity
+}  // namespace hardware
+}  // namespace android
diff --git a/identity/support/src/cppbor.cpp b/identity/support/src/cppbor.cpp
new file mode 100644
index 0000000..d289985
--- /dev/null
+++ b/identity/support/src/cppbor.cpp
@@ -0,0 +1,225 @@
+/*
+ * 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 "cppbor.h"
+#include "cppbor_parse.h"
+
+#define LOG_TAG "CppBor"
+#include <android-base/logging.h>
+
+namespace cppbor {
+
+namespace {
+
+template <typename T, typename Iterator, typename = std::enable_if<std::is_unsigned<T>::value>>
+Iterator writeBigEndian(T value, Iterator pos) {
+    for (unsigned i = 0; i < sizeof(value); ++i) {
+        *pos++ = static_cast<uint8_t>(value >> (8 * (sizeof(value) - 1)));
+        value = static_cast<T>(value << 8);
+    }
+    return pos;
+}
+
+template <typename T, typename = std::enable_if<std::is_unsigned<T>::value>>
+void writeBigEndian(T value, std::function<void(uint8_t)>& cb) {
+    for (unsigned i = 0; i < sizeof(value); ++i) {
+        cb(static_cast<uint8_t>(value >> (8 * (sizeof(value) - 1))));
+        value = static_cast<T>(value << 8);
+    }
+}
+
+}  // namespace
+
+size_t headerSize(uint64_t addlInfo) {
+    if (addlInfo < ONE_BYTE_LENGTH) return 1;
+    if (addlInfo <= std::numeric_limits<uint8_t>::max()) return 2;
+    if (addlInfo <= std::numeric_limits<uint16_t>::max()) return 3;
+    if (addlInfo <= std::numeric_limits<uint32_t>::max()) return 5;
+    return 9;
+}
+
+uint8_t* encodeHeader(MajorType type, uint64_t addlInfo, uint8_t* pos, const uint8_t* end) {
+    size_t sz = headerSize(addlInfo);
+    if (end - pos < static_cast<ssize_t>(sz)) return nullptr;
+    switch (sz) {
+        case 1:
+            *pos++ = type | static_cast<uint8_t>(addlInfo);
+            return pos;
+        case 2:
+            *pos++ = type | ONE_BYTE_LENGTH;
+            *pos++ = static_cast<uint8_t>(addlInfo);
+            return pos;
+        case 3:
+            *pos++ = type | TWO_BYTE_LENGTH;
+            return writeBigEndian(static_cast<uint16_t>(addlInfo), pos);
+        case 5:
+            *pos++ = type | FOUR_BYTE_LENGTH;
+            return writeBigEndian(static_cast<uint32_t>(addlInfo), pos);
+        case 9:
+            *pos++ = type | EIGHT_BYTE_LENGTH;
+            return writeBigEndian(addlInfo, pos);
+        default:
+            CHECK(false);  // Impossible to get here.
+            return nullptr;
+    }
+}
+
+void encodeHeader(MajorType type, uint64_t addlInfo, EncodeCallback encodeCallback) {
+    size_t sz = headerSize(addlInfo);
+    switch (sz) {
+        case 1:
+            encodeCallback(type | static_cast<uint8_t>(addlInfo));
+            break;
+        case 2:
+            encodeCallback(type | ONE_BYTE_LENGTH);
+            encodeCallback(static_cast<uint8_t>(addlInfo));
+            break;
+        case 3:
+            encodeCallback(type | TWO_BYTE_LENGTH);
+            writeBigEndian(static_cast<uint16_t>(addlInfo), encodeCallback);
+            break;
+        case 5:
+            encodeCallback(type | FOUR_BYTE_LENGTH);
+            writeBigEndian(static_cast<uint32_t>(addlInfo), encodeCallback);
+            break;
+        case 9:
+            encodeCallback(type | EIGHT_BYTE_LENGTH);
+            writeBigEndian(addlInfo, encodeCallback);
+            break;
+        default:
+            CHECK(false);  // Impossible to get here.
+    }
+}
+
+bool Item::operator==(const Item& other) const& {
+    if (type() != other.type()) return false;
+    switch (type()) {
+        case UINT:
+            return *asUint() == *(other.asUint());
+        case NINT:
+            return *asNint() == *(other.asNint());
+        case BSTR:
+            return *asBstr() == *(other.asBstr());
+        case TSTR:
+            return *asTstr() == *(other.asTstr());
+        case ARRAY:
+            return *asArray() == *(other.asArray());
+        case MAP:
+            return *asMap() == *(other.asMap());
+        case SIMPLE:
+            return *asSimple() == *(other.asSimple());
+        case SEMANTIC:
+            return *asSemantic() == *(other.asSemantic());
+        default:
+            CHECK(false);  // Impossible to get here.
+            return false;
+    }
+}
+
+Nint::Nint(int64_t v) : mValue(v) {
+    CHECK(v < 0) << "Only negative values allowed";
+}
+
+bool Simple::operator==(const Simple& other) const& {
+    if (simpleType() != other.simpleType()) return false;
+
+    switch (simpleType()) {
+        case BOOLEAN:
+            return *asBool() == *(other.asBool());
+        case NULL_T:
+            return true;
+        default:
+            CHECK(false);  // Impossible to get here.
+            return false;
+    }
+}
+
+uint8_t* Bstr::encode(uint8_t* pos, const uint8_t* end) const {
+    pos = encodeHeader(mValue.size(), pos, end);
+    if (!pos || end - pos < static_cast<ptrdiff_t>(mValue.size())) return nullptr;
+    return std::copy(mValue.begin(), mValue.end(), pos);
+}
+
+void Bstr::encodeValue(EncodeCallback encodeCallback) const {
+    for (auto c : mValue) {
+        encodeCallback(c);
+    }
+}
+
+uint8_t* Tstr::encode(uint8_t* pos, const uint8_t* end) const {
+    pos = encodeHeader(mValue.size(), pos, end);
+    if (!pos || end - pos < static_cast<ptrdiff_t>(mValue.size())) return nullptr;
+    return std::copy(mValue.begin(), mValue.end(), pos);
+}
+
+void Tstr::encodeValue(EncodeCallback encodeCallback) const {
+    for (auto c : mValue) {
+        encodeCallback(static_cast<uint8_t>(c));
+    }
+}
+
+bool CompoundItem::operator==(const CompoundItem& other) const& {
+    return type() == other.type()             //
+           && addlInfo() == other.addlInfo()  //
+           // Can't use vector::operator== because the contents are pointers.  std::equal lets us
+           // provide a predicate that does the dereferencing.
+           && std::equal(mEntries.begin(), mEntries.end(), other.mEntries.begin(),
+                         [](auto& a, auto& b) -> bool { return *a == *b; });
+}
+
+uint8_t* CompoundItem::encode(uint8_t* pos, const uint8_t* end) const {
+    pos = encodeHeader(addlInfo(), pos, end);
+    if (!pos) return nullptr;
+    for (auto& entry : mEntries) {
+        pos = entry->encode(pos, end);
+        if (!pos) return nullptr;
+    }
+    return pos;
+}
+
+void CompoundItem::encode(EncodeCallback encodeCallback) const {
+    encodeHeader(addlInfo(), encodeCallback);
+    for (auto& entry : mEntries) {
+        entry->encode(encodeCallback);
+    }
+}
+
+void Map::assertInvariant() const {
+    CHECK(mEntries.size() % 2 == 0);
+}
+
+std::unique_ptr<Item> Map::clone() const {
+    assertInvariant();
+    auto res = std::make_unique<Map>();
+    for (size_t i = 0; i < mEntries.size(); i += 2) {
+        res->add(mEntries[i]->clone(), mEntries[i + 1]->clone());
+    }
+    return res;
+}
+
+std::unique_ptr<Item> Array::clone() const {
+    auto res = std::make_unique<Array>();
+    for (size_t i = 0; i < mEntries.size(); i++) {
+        res->add(mEntries[i]->clone());
+    }
+    return res;
+}
+
+void Semantic::assertInvariant() const {
+    CHECK(mEntries.size() == 1);
+}
+
+}  // namespace cppbor
diff --git a/identity/support/src/cppbor_parse.cpp b/identity/support/src/cppbor_parse.cpp
new file mode 100644
index 0000000..c9ebb8a
--- /dev/null
+++ b/identity/support/src/cppbor_parse.cpp
@@ -0,0 +1,351 @@
+/*
+ * 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 "cppbor_parse.h"
+
+#include <sstream>
+#include <stack>
+
+#define LOG_TAG "CppBor"
+#include <android-base/logging.h>
+
+namespace cppbor {
+
+namespace {
+
+std::string insufficientLengthString(size_t bytesNeeded, size_t bytesAvail,
+                                     const std::string& type) {
+    std::stringstream errStream;
+    errStream << "Need " << bytesNeeded << " byte(s) for " << type << ", have " << bytesAvail
+              << ".";
+    return errStream.str();
+}
+
+template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
+std::tuple<bool, uint64_t, const uint8_t*> parseLength(const uint8_t* pos, const uint8_t* end,
+                                                       ParseClient* parseClient) {
+    if (pos + sizeof(T) > end) {
+        parseClient->error(pos - 1, insufficientLengthString(sizeof(T), end - pos, "length field"));
+        return {false, 0, pos};
+    }
+
+    const uint8_t* intEnd = pos + sizeof(T);
+    T result = 0;
+    do {
+        result = static_cast<T>((result << 8) | *pos++);
+    } while (pos < intEnd);
+    return {true, result, pos};
+}
+
+std::tuple<const uint8_t*, ParseClient*> parseRecursively(const uint8_t* begin, const uint8_t* end,
+                                                          ParseClient* parseClient);
+
+std::tuple<const uint8_t*, ParseClient*> handleUint(uint64_t value, const uint8_t* hdrBegin,
+                                                    const uint8_t* hdrEnd,
+                                                    ParseClient* parseClient) {
+    std::unique_ptr<Item> item = std::make_unique<Uint>(value);
+    return {hdrEnd,
+            parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)};
+}
+
+std::tuple<const uint8_t*, ParseClient*> handleNint(uint64_t value, const uint8_t* hdrBegin,
+                                                    const uint8_t* hdrEnd,
+                                                    ParseClient* parseClient) {
+    if (value > std::numeric_limits<int64_t>::max()) {
+        parseClient->error(hdrBegin, "NINT values that don't fit in int64_t are not supported.");
+        return {hdrBegin, nullptr /* end parsing */};
+    }
+    std::unique_ptr<Item> item = std::make_unique<Nint>(-1 - static_cast<uint64_t>(value));
+    return {hdrEnd,
+            parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)};
+}
+
+std::tuple<const uint8_t*, ParseClient*> handleBool(uint64_t value, const uint8_t* hdrBegin,
+                                                    const uint8_t* hdrEnd,
+                                                    ParseClient* parseClient) {
+    std::unique_ptr<Item> item = std::make_unique<Bool>(value == TRUE);
+    return {hdrEnd,
+            parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)};
+}
+
+std::tuple<const uint8_t*, ParseClient*> handleNull(const uint8_t* hdrBegin, const uint8_t* hdrEnd,
+                                                    ParseClient* parseClient) {
+    std::unique_ptr<Item> item = std::make_unique<Null>();
+    return {hdrEnd,
+            parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)};
+}
+
+template <typename T>
+std::tuple<const uint8_t*, ParseClient*> handleString(uint64_t length, const uint8_t* hdrBegin,
+                                                      const uint8_t* valueBegin, const uint8_t* end,
+                                                      const std::string& errLabel,
+                                                      ParseClient* parseClient) {
+    if (end - valueBegin < static_cast<ssize_t>(length)) {
+        parseClient->error(hdrBegin, insufficientLengthString(length, end - valueBegin, errLabel));
+        return {hdrBegin, nullptr /* end parsing */};
+    }
+
+    std::unique_ptr<Item> item = std::make_unique<T>(valueBegin, valueBegin + length);
+    return {valueBegin + length,
+            parseClient->item(item, hdrBegin, valueBegin, valueBegin + length)};
+}
+
+class IncompleteItem {
+  public:
+    virtual ~IncompleteItem() {}
+    virtual void add(std::unique_ptr<Item> item) = 0;
+};
+
+class IncompleteArray : public Array, public IncompleteItem {
+  public:
+    IncompleteArray(size_t size) : mSize(size) {}
+
+    // We return the "complete" size, rather than the actual size.
+    size_t size() const override { return mSize; }
+
+    void add(std::unique_ptr<Item> item) override {
+        mEntries.reserve(mSize);
+        mEntries.push_back(std::move(item));
+    }
+
+  private:
+    size_t mSize;
+};
+
+class IncompleteMap : public Map, public IncompleteItem {
+  public:
+    IncompleteMap(size_t size) : mSize(size) {}
+
+    // We return the "complete" size, rather than the actual size.
+    size_t size() const override { return mSize; }
+
+    void add(std::unique_ptr<Item> item) override {
+        mEntries.reserve(mSize * 2);
+        mEntries.push_back(std::move(item));
+    }
+
+  private:
+    size_t mSize;
+};
+
+class IncompleteSemantic : public Semantic, public IncompleteItem {
+  public:
+    IncompleteSemantic(uint64_t value) : Semantic(value) {}
+
+    // We return the "complete" size, rather than the actual size.
+    size_t size() const override { return 1; }
+
+    void add(std::unique_ptr<Item> item) override {
+        mEntries.reserve(1);
+        mEntries.push_back(std::move(item));
+    }
+};
+
+std::tuple<const uint8_t*, ParseClient*> handleEntries(size_t entryCount, const uint8_t* hdrBegin,
+                                                       const uint8_t* pos, const uint8_t* end,
+                                                       const std::string& typeName,
+                                                       ParseClient* parseClient) {
+    while (entryCount > 0) {
+        --entryCount;
+        if (pos == end) {
+            parseClient->error(hdrBegin, "Not enough entries for " + typeName + ".");
+            return {hdrBegin, nullptr /* end parsing */};
+        }
+        std::tie(pos, parseClient) = parseRecursively(pos, end, parseClient);
+        if (!parseClient) return {hdrBegin, nullptr};
+    }
+    return {pos, parseClient};
+}
+
+std::tuple<const uint8_t*, ParseClient*> handleCompound(
+        std::unique_ptr<Item> item, uint64_t entryCount, const uint8_t* hdrBegin,
+        const uint8_t* valueBegin, const uint8_t* end, const std::string& typeName,
+        ParseClient* parseClient) {
+    parseClient =
+            parseClient->item(item, hdrBegin, valueBegin, valueBegin /* don't know the end yet */);
+    if (!parseClient) return {hdrBegin, nullptr};
+
+    const uint8_t* pos;
+    std::tie(pos, parseClient) =
+            handleEntries(entryCount, hdrBegin, valueBegin, end, typeName, parseClient);
+    if (!parseClient) return {hdrBegin, nullptr};
+
+    return {pos, parseClient->itemEnd(item, hdrBegin, valueBegin, pos)};
+}
+
+std::tuple<const uint8_t*, ParseClient*> parseRecursively(const uint8_t* begin, const uint8_t* end,
+                                                          ParseClient* parseClient) {
+    const uint8_t* pos = begin;
+
+    MajorType type = static_cast<MajorType>(*pos & 0xE0);
+    uint8_t tagInt = *pos & 0x1F;
+    ++pos;
+
+    bool success = true;
+    uint64_t addlData;
+    if (tagInt < ONE_BYTE_LENGTH || tagInt > EIGHT_BYTE_LENGTH) {
+        addlData = tagInt;
+    } else {
+        switch (tagInt) {
+            case ONE_BYTE_LENGTH:
+                std::tie(success, addlData, pos) = parseLength<uint8_t>(pos, end, parseClient);
+                break;
+
+            case TWO_BYTE_LENGTH:
+                std::tie(success, addlData, pos) = parseLength<uint16_t>(pos, end, parseClient);
+                break;
+
+            case FOUR_BYTE_LENGTH:
+                std::tie(success, addlData, pos) = parseLength<uint32_t>(pos, end, parseClient);
+                break;
+
+            case EIGHT_BYTE_LENGTH:
+                std::tie(success, addlData, pos) = parseLength<uint64_t>(pos, end, parseClient);
+                break;
+
+            default:
+                CHECK(false);  //  It's impossible to get here
+                break;
+        }
+    }
+
+    if (!success) return {begin, nullptr};
+
+    switch (type) {
+        case UINT:
+            return handleUint(addlData, begin, pos, parseClient);
+
+        case NINT:
+            return handleNint(addlData, begin, pos, parseClient);
+
+        case BSTR:
+            return handleString<Bstr>(addlData, begin, pos, end, "byte string", parseClient);
+
+        case TSTR:
+            return handleString<Tstr>(addlData, begin, pos, end, "text string", parseClient);
+
+        case ARRAY:
+            return handleCompound(std::make_unique<IncompleteArray>(addlData), addlData, begin, pos,
+                                  end, "array", parseClient);
+
+        case MAP:
+            return handleCompound(std::make_unique<IncompleteMap>(addlData), addlData * 2, begin,
+                                  pos, end, "map", parseClient);
+
+        case SEMANTIC:
+            return handleCompound(std::make_unique<IncompleteSemantic>(addlData), 1, begin, pos,
+                                  end, "semantic", parseClient);
+
+        case SIMPLE:
+            switch (addlData) {
+                case TRUE:
+                case FALSE:
+                    return handleBool(addlData, begin, pos, parseClient);
+                case NULL_V:
+                    return handleNull(begin, pos, parseClient);
+            }
+    }
+    CHECK(false);  // Impossible to get here.
+    return {};
+}
+
+class FullParseClient : public ParseClient {
+  public:
+    virtual ParseClient* item(std::unique_ptr<Item>& item, const uint8_t*, const uint8_t*,
+                              const uint8_t* end) override {
+        if (mParentStack.empty() && !item->isCompound()) {
+            // This is the first and only item.
+            mTheItem = std::move(item);
+            mPosition = end;
+            return nullptr;  //  We're done.
+        }
+
+        if (item->isCompound()) {
+            // Starting a new compound data item, i.e. a new parent.  Save it on the parent stack.
+            // It's safe to save a raw pointer because the unique_ptr is guaranteed to stay in
+            // existence until the corresponding itemEnd() call.
+            assert(dynamic_cast<CompoundItem*>(item.get()));
+            mParentStack.push(static_cast<CompoundItem*>(item.get()));
+            return this;
+        } else {
+            appendToLastParent(std::move(item));
+            return this;
+        }
+    }
+
+    virtual ParseClient* itemEnd(std::unique_ptr<Item>& item, const uint8_t*, const uint8_t*,
+                                 const uint8_t* end) override {
+        CHECK(item->isCompound() && item.get() == mParentStack.top());
+        mParentStack.pop();
+
+        if (mParentStack.empty()) {
+            mTheItem = std::move(item);
+            mPosition = end;
+            return nullptr;  // We're done
+        } else {
+            appendToLastParent(std::move(item));
+            return this;
+        }
+    }
+
+    virtual void error(const uint8_t* position, const std::string& errorMessage) override {
+        mPosition = position;
+        mErrorMessage = errorMessage;
+    }
+
+    std::tuple<std::unique_ptr<Item> /* result */, const uint8_t* /* newPos */,
+               std::string /* errMsg */>
+    parseResult() {
+        std::unique_ptr<Item> p = std::move(mTheItem);
+        return {std::move(p), mPosition, std::move(mErrorMessage)};
+    }
+
+  private:
+    void appendToLastParent(std::unique_ptr<Item> item) {
+        auto parent = mParentStack.top();
+        assert(dynamic_cast<IncompleteItem*>(parent));
+        if (parent->type() == ARRAY) {
+            static_cast<IncompleteArray*>(parent)->add(std::move(item));
+        } else if (parent->type() == MAP) {
+            static_cast<IncompleteMap*>(parent)->add(std::move(item));
+        } else if (parent->type() == SEMANTIC) {
+            static_cast<IncompleteSemantic*>(parent)->add(std::move(item));
+        } else {
+            CHECK(false);  // Impossible to get here.
+        }
+    }
+
+    std::unique_ptr<Item> mTheItem;
+    std::stack<CompoundItem*> mParentStack;
+    const uint8_t* mPosition = nullptr;
+    std::string mErrorMessage;
+};
+
+}  // anonymous namespace
+
+void parse(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient) {
+    parseRecursively(begin, end, parseClient);
+}
+
+std::tuple<std::unique_ptr<Item> /* result */, const uint8_t* /* newPos */,
+           std::string /* errMsg */>
+parse(const uint8_t* begin, const uint8_t* end) {
+    FullParseClient parseClient;
+    parse(begin, end, &parseClient);
+    return parseClient.parseResult();
+}
+
+}  // namespace cppbor
diff --git a/identity/support/tests/IdentityCredentialSupportTest.cpp b/identity/support/tests/IdentityCredentialSupportTest.cpp
new file mode 100644
index 0000000..c356549
--- /dev/null
+++ b/identity/support/tests/IdentityCredentialSupportTest.cpp
@@ -0,0 +1,446 @@
+/*
+ * 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 <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+using std::optional;
+using std::string;
+using std::vector;
+
+namespace android {
+namespace hardware {
+namespace identity {
+
+TEST(IdentityCredentialSupport, encodeHex) {
+    EXPECT_EQ("", support::encodeHex(vector<uint8_t>({})));
+    EXPECT_EQ("01", support::encodeHex(vector<uint8_t>({1})));
+    EXPECT_EQ("000102030405060708090a0b0c0d0e0f10",
+              support::encodeHex(
+                      vector<uint8_t>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16})));
+    EXPECT_EQ("0102ffe060", support::encodeHex(vector<uint8_t>({1, 2, 255, 224, 96})));
+}
+
+TEST(IdentityCredentialSupport, decodeHex) {
+    EXPECT_EQ(vector<uint8_t>({}), support::decodeHex(""));
+    EXPECT_EQ(vector<uint8_t>({1}), support::decodeHex("01"));
+
+    EXPECT_EQ(vector<uint8_t>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}),
+              support::decodeHex("000102030405060708090a0b0c0d0e0f10"));
+
+    EXPECT_FALSE(support::decodeHex("0g"));
+    EXPECT_FALSE(support::decodeHex("0"));
+    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};
+
+    optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
+    ASSERT_TRUE(keyPair);
+    optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
+    ASSERT_TRUE(privKey);
+    optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
+    ASSERT_TRUE(pubKey);
+
+    optional<vector<uint8_t>> signature = support::signEcDsa(privKey.value(), data);
+    ASSERT_TRUE(
+            support::checkEcDsaSignature(support::sha256(data), signature.value(), pubKey.value()));
+
+    // Manipulate the signature, check that verification fails.
+    vector<uint8_t> modifiedSignature = signature.value();
+    modifiedSignature[0] ^= 0xff;
+    ASSERT_FALSE(
+            support::checkEcDsaSignature(support::sha256(data), modifiedSignature, pubKey.value()));
+
+    // Manipulate the data being checked, check that verification fails.
+    vector<uint8_t> modifiedDigest = support::sha256(data);
+    modifiedDigest[0] ^= 0xff;
+    ASSERT_FALSE(support::checkEcDsaSignature(modifiedDigest, signature.value(), pubKey.value()));
+}
+
+string replaceLine(const string& str, ssize_t lineNumber, const string& replacement) {
+    vector<string> lines;
+    std::istringstream f(str);
+    string s;
+    while (std::getline(f, s, '\n')) {
+        lines.push_back(s);
+    }
+
+    size_t numLines = lines.size();
+    if (lineNumber < 0) {
+        lineNumber = numLines - (-lineNumber);
+    }
+
+    string ret;
+    size_t n = 0;
+    for (const string& line : lines) {
+        if (n == lineNumber) {
+            ret += replacement + "\n";
+        } else {
+            ret += line + "\n";
+        }
+        n++;
+    }
+    return ret;
+}
+
+TEST(IdentityCredentialSupport, CoseSignatures) {
+    optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
+    ASSERT_TRUE(keyPair);
+    optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
+    ASSERT_TRUE(privKey);
+    optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
+    ASSERT_TRUE(pubKey);
+
+    vector<uint8_t> data = {1, 2, 3};
+    optional<vector<uint8_t>> coseSign1 = support::coseSignEcDsa(
+            privKey.value(), data, {} /* detachedContent */, {} /* x5chain */);
+    ASSERT_TRUE(support::coseCheckEcDsaSignature(coseSign1.value(), {} /* detachedContent */,
+                                                 pubKey.value()));
+
+    optional<vector<uint8_t>> payload = support::coseSignGetPayload(coseSign1.value());
+    ASSERT_TRUE(payload);
+    ASSERT_EQ(data, payload.value());
+
+    // Finally, check that |coseSign1| are the bytes of a valid COSE_Sign1 message
+    string out = support::cborPrettyPrint(coseSign1.value());
+    out = replaceLine(out, -2, "  [] // Signature Removed");
+    EXPECT_EQ(
+            "[\n"
+            "  {0xa1, 0x01, 0x26},\n"  // Bytes of {1:-7} 1 is 'alg' label and -7 is "ECDSA 256"
+            "  {},\n"
+            "  {0x01, 0x02, 0x03},\n"
+            "  [] // Signature Removed\n"
+            "]\n",
+            out);
+}
+
+TEST(IdentityCredentialSupport, CoseSignaturesAdditionalData) {
+    optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
+    ASSERT_TRUE(keyPair);
+    optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
+    ASSERT_TRUE(privKey);
+    optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
+    ASSERT_TRUE(pubKey);
+
+    vector<uint8_t> detachedContent = {1, 2, 3};
+    optional<vector<uint8_t>> coseSign1 = support::coseSignEcDsa(privKey.value(), {} /* data */,
+                                                                 detachedContent, {} /* x5chain */);
+    ASSERT_TRUE(
+            support::coseCheckEcDsaSignature(coseSign1.value(), detachedContent, pubKey.value()));
+
+    optional<vector<uint8_t>> payload = support::coseSignGetPayload(coseSign1.value());
+    ASSERT_TRUE(payload);
+    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());
+    out = replaceLine(out, -2, "  [] // Signature Removed");
+    EXPECT_EQ(
+            "[\n"
+            "  {0xa1, 0x01, 0x26},\n"  // Bytes of {1:-7} 1 is 'alg' label and -7 is "ECDSA 256"
+            "  {},\n"
+            "  null,\n"
+            "  [] // Signature Removed\n"
+            "]\n",
+            out);
+}
+
+vector<uint8_t> generateCertChain(size_t numCerts) {
+    vector<vector<uint8_t>> certs;
+
+    for (size_t n = 0; n < numCerts; n++) {
+        optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
+        optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
+        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);
+        certs.push_back(cert.value());
+    }
+    return support::certificateChainJoin(certs);
+}
+
+TEST(IdentityCredentialSupport, CoseSignaturesX5ChainWithSingleCert) {
+    optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
+    ASSERT_TRUE(keyPair);
+    optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
+    ASSERT_TRUE(privKey);
+    optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
+    ASSERT_TRUE(pubKey);
+
+    vector<uint8_t> certChain = generateCertChain(1);
+    optional<vector<vector<uint8_t>>> splitCerts = support::certificateChainSplit(certChain);
+    ASSERT_EQ(1, splitCerts.value().size());
+
+    vector<uint8_t> detachedContent = {1, 2, 3};
+    optional<vector<uint8_t>> coseSign1 =
+            support::coseSignEcDsa(privKey.value(), {} /* data */, detachedContent, certChain);
+    ASSERT_TRUE(
+            support::coseCheckEcDsaSignature(coseSign1.value(), detachedContent, pubKey.value()));
+
+    optional<vector<uint8_t>> payload = support::coseSignGetPayload(coseSign1.value());
+    ASSERT_TRUE(payload);
+    ASSERT_EQ(0, payload.value().size());
+
+    optional<vector<uint8_t>> certsRecovered = support::coseSignGetX5Chain(coseSign1.value());
+    EXPECT_EQ(certsRecovered.value(), certChain);
+}
+
+TEST(IdentityCredentialSupport, CoseSignaturesX5ChainWithMultipleCerts) {
+    optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
+    ASSERT_TRUE(keyPair);
+    optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
+    ASSERT_TRUE(privKey);
+    optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
+    ASSERT_TRUE(pubKey);
+
+    vector<uint8_t> certChain = generateCertChain(5);
+    optional<vector<vector<uint8_t>>> splitCerts = support::certificateChainSplit(certChain);
+    ASSERT_EQ(5, splitCerts.value().size());
+
+    vector<uint8_t> detachedContent = {1, 2, 3};
+    optional<vector<uint8_t>> coseSign1 =
+            support::coseSignEcDsa(privKey.value(), {} /* data */, detachedContent, certChain);
+    ASSERT_TRUE(
+            support::coseCheckEcDsaSignature(coseSign1.value(), detachedContent, pubKey.value()));
+
+    optional<vector<uint8_t>> payload = support::coseSignGetPayload(coseSign1.value());
+    ASSERT_TRUE(payload);
+    ASSERT_EQ(0, payload.value().size());
+
+    optional<vector<uint8_t>> certsRecovered = support::coseSignGetX5Chain(coseSign1.value());
+    EXPECT_EQ(certsRecovered.value(), certChain);
+}
+
+TEST(IdentityCredentialSupport, CertificateChain) {
+    optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
+    ASSERT_TRUE(keyPair);
+    optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
+    ASSERT_TRUE(privKey);
+    optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
+    ASSERT_TRUE(pubKey);
+
+    optional<vector<uint8_t>> cert = support::ecPublicKeyGenerateCertificate(
+            pubKey.value(), privKey.value(), "0001", "someIssuer", "someSubject", 0, 0);
+
+    optional<vector<uint8_t>> extractedPubKey =
+            support::certificateChainGetTopMostKey(cert.value());
+    ASSERT_TRUE(extractedPubKey);
+    ASSERT_EQ(pubKey.value(), extractedPubKey.value());
+
+    // We expect to the chain returned by ecPublicKeyGenerateCertificate() to only have a
+    // single element
+    optional<vector<vector<uint8_t>>> splitCerts = support::certificateChainSplit(cert.value());
+    ASSERT_EQ(1, splitCerts.value().size());
+    ASSERT_EQ(splitCerts.value()[0], cert.value());
+
+    optional<vector<uint8_t>> otherKeyPair = support::createEcKeyPair();
+    ASSERT_TRUE(otherKeyPair);
+    optional<vector<uint8_t>> otherPrivKey = support::ecKeyPairGetPrivateKey(keyPair.value());
+    ASSERT_TRUE(otherPrivKey);
+    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);
+
+    // Now both cert and otherCert are two distinct certificates. Let's make a
+    // chain and check that certificateChainSplit() works as expected.
+    ASSERT_NE(cert.value(), otherCert.value());
+    const vector<vector<uint8_t>> certs2 = {cert.value(), otherCert.value()};
+    vector<uint8_t> certs2combined = support::certificateChainJoin(certs2);
+    ASSERT_EQ(certs2combined.size(), cert.value().size() + otherCert.value().size());
+    optional<vector<vector<uint8_t>>> splitCerts2 = support::certificateChainSplit(certs2combined);
+    ASSERT_EQ(certs2, splitCerts2.value());
+}
+
+vector<uint8_t> strToVec(const string& str) {
+    vector<uint8_t> ret;
+    size_t size = str.size();
+    ret.resize(size);
+    memcpy(ret.data(), str.data(), size);
+    return ret;
+}
+
+// Test vector from https://en.wikipedia.org/wiki/HMAC
+TEST(IdentityCredentialSupport, hmacSha256) {
+    vector<uint8_t> key = strToVec("key");
+    vector<uint8_t> data = strToVec("The quick brown fox jumps over the lazy dog");
+
+    vector<uint8_t> expected =
+            support::decodeHex("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8")
+                    .value();
+
+    optional<vector<uint8_t>> hmac = support::hmacSha256(key, data);
+    ASSERT_TRUE(hmac);
+    ASSERT_EQ(expected, hmac.value());
+}
+
+// See also CoseMac0 test in UtilUnitTest.java inside cts/tests/tests/identity/
+TEST(IdentityCredentialSupport, CoseMac0) {
+    vector<uint8_t> key;
+    key.resize(32);
+    vector<uint8_t> data = {0x10, 0x11, 0x12, 0x13};
+    vector<uint8_t> detachedContent = {};
+
+    optional<vector<uint8_t>> mac = support::coseMac0(key, data, detachedContent);
+    ASSERT_TRUE(mac);
+
+    EXPECT_EQ(
+            "[\n"
+            "  {0xa1, 0x01, 0x05},\n"
+            "  {},\n"
+            "  {0x10, 0x11, 0x12, 0x13},\n"
+            "  {0x6c, 0xec, 0xb5, 0x6a, 0xc9, 0x5c, 0xae, 0x3b, 0x41, 0x13, 0xde, 0xa4, 0xd8, "
+            "0x86, 0x5c, 0x28, 0x2c, 0xd5, 0xa5, 0x13, 0xff, 0x3b, 0xd1, 0xde, 0x70, 0x5e, 0xbb, "
+            "0xe2, 0x2d, 0x42, 0xbe, 0x53},\n"
+            "]",
+            support::cborPrettyPrint(mac.value()));
+}
+
+TEST(IdentityCredentialSupport, CoseMac0DetachedContent) {
+    vector<uint8_t> key;
+    key.resize(32);
+    vector<uint8_t> data = {};
+    vector<uint8_t> detachedContent = {0x10, 0x11, 0x12, 0x13};
+
+    optional<vector<uint8_t>> mac = support::coseMac0(key, data, detachedContent);
+    ASSERT_TRUE(mac);
+
+    // Same HMAC as in CoseMac0 test, only difference is that payload is null.
+    EXPECT_EQ(
+            "[\n"
+            "  {0xa1, 0x01, 0x05},\n"
+            "  {},\n"
+            "  null,\n"
+            "  {0x6c, 0xec, 0xb5, 0x6a, 0xc9, 0x5c, 0xae, 0x3b, 0x41, 0x13, 0xde, 0xa4, 0xd8, "
+            "0x86, 0x5c, 0x28, 0x2c, 0xd5, 0xa5, 0x13, 0xff, 0x3b, 0xd1, 0xde, 0x70, 0x5e, 0xbb, "
+            "0xe2, 0x2d, 0x42, 0xbe, 0x53},\n"
+            "]",
+            support::cborPrettyPrint(mac.value()));
+}
+
+}  // namespace identity
+}  // namespace hardware
+}  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/identity/support/tests/cppbor_test.cpp b/identity/support/tests/cppbor_test.cpp
new file mode 100644
index 0000000..3eb5598
--- /dev/null
+++ b/identity/support/tests/cppbor_test.cpp
@@ -0,0 +1,1013 @@
+/*
+ * 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 <iomanip>
+#include <sstream>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "cppbor.h"
+#include "cppbor_parse.h"
+
+using namespace cppbor;
+using namespace std;
+
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::ByRef;
+using ::testing::InSequence;
+using ::testing::IsNull;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::Truly;
+using ::testing::Unused;
+
+string hexDump(const string& str) {
+    stringstream s;
+    for (auto c : str) {
+        s << setfill('0') << setw(2) << hex << (static_cast<unsigned>(c) & 0xff);
+    }
+    return s.str();
+}
+
+TEST(SimpleValueTest, UnsignedValueSizes) {
+    // Check that unsigned integers encode to correct lengths, and that encodedSize() is correct.
+    vector<pair<uint64_t /* value */, size_t /* expected encoded size */>> testCases{
+            {0, 1},
+            {1, 1},
+            {23, 1},
+            {24, 2},
+            {255, 2},
+            {256, 3},
+            {65535, 3},
+            {65536, 5},
+            {4294967295, 5},
+            {4294967296, 9},
+            {std::numeric_limits<uint64_t>::max(), 9},
+    };
+    for (auto& testCase : testCases) {
+        Uint val(testCase.first);
+        EXPECT_EQ(testCase.second, val.encodedSize()) << "Wrong size for value " << testCase.first;
+        EXPECT_EQ(val.encodedSize(), val.toString().size())
+                << "encodedSize and encoding disagree for value " << testCase.first;
+    }
+}
+
+TEST(SimpleValueTest, UnsignedValueEncodings) {
+    EXPECT_EQ("\x00"s, Uint(0u).toString());
+    EXPECT_EQ("\x01"s, Uint(1u).toString());
+    EXPECT_EQ("\x0a"s, Uint(10u).toString());
+    EXPECT_EQ("\x17"s, Uint(23u).toString());
+    EXPECT_EQ("\x18\x18"s, Uint(24u).toString());
+    EXPECT_EQ("\x18\x19"s, Uint(25u).toString());
+    EXPECT_EQ("\x18\x64"s, Uint(100u).toString());
+    EXPECT_EQ("\x19\x03\xe8"s, Uint(1000u).toString());
+    EXPECT_EQ("\x1a\x00\x0f\x42\x40"s, Uint(1000000u).toString());
+    EXPECT_EQ("\x1b\x00\x00\x00\xe8\xd4\xa5\x10\x00"s, Uint(1000000000000u).toString());
+    EXPECT_EQ("\x1B\x7f\xff\xff\xff\xff\xff\xff\xff"s,
+              Uint(std::numeric_limits<int64_t>::max()).toString());
+}
+
+TEST(SimpleValueTest, NegativeValueEncodings) {
+    EXPECT_EQ("\x20"s, Nint(-1).toString());
+    EXPECT_EQ("\x28"s, Nint(-9).toString());
+    EXPECT_EQ("\x29"s, Nint(-10).toString());
+    EXPECT_EQ("\x36"s, Nint(-23).toString());
+    EXPECT_EQ("\x37"s, Nint(-24).toString());
+    EXPECT_EQ("\x38\x18"s, Nint(-25).toString());
+    EXPECT_EQ("\x38\x62"s, Nint(-99).toString());
+    EXPECT_EQ("\x38\x63"s, Nint(-100).toString());
+    EXPECT_EQ("\x39\x03\xe6"s, Nint(-999).toString());
+    EXPECT_EQ("\x39\x03\xe7"s, Nint(-1000).toString());
+    EXPECT_EQ("\x3a\x00\x0f\x42\x3F"s, Nint(-1000000).toString());
+    EXPECT_EQ("\x3b\x00\x00\x00\xe8\xd4\xa5\x0f\xff"s, Nint(-1000000000000).toString());
+    EXPECT_EQ("\x3B\x7f\xff\xff\xff\xff\xff\xff\xff"s,
+              Nint(std::numeric_limits<int64_t>::min()).toString());
+}
+
+TEST(SimpleValueDeathTest, NegativeValueEncodings) {
+    EXPECT_DEATH(Nint(0), "");
+    EXPECT_DEATH(Nint(1), "");
+}
+
+TEST(SimpleValueTest, BooleanEncodings) {
+    EXPECT_EQ("\xf4"s, Bool(false).toString());
+    EXPECT_EQ("\xf5"s, Bool(true).toString());
+}
+
+TEST(SimpleValueTest, ByteStringEncodings) {
+    EXPECT_EQ("\x40", Bstr("").toString());
+    EXPECT_EQ("\x41\x61", Bstr("a").toString());
+    EXPECT_EQ("\x41\x41", Bstr("A").toString());
+    EXPECT_EQ("\x44\x49\x45\x54\x46", Bstr("IETF").toString());
+    EXPECT_EQ("\x42\x22\x5c", Bstr("\"\\").toString());
+    EXPECT_EQ("\x42\xc3\xbc", Bstr("\xc3\xbc").toString());
+    EXPECT_EQ("\x43\xe6\xb0\xb4", Bstr("\xe6\xb0\xb4").toString());
+    EXPECT_EQ("\x44\xf0\x90\x85\x91", Bstr("\xf0\x90\x85\x91").toString());
+    EXPECT_EQ("\x44\x01\x02\x03\x04", Bstr("\x01\x02\x03\x04").toString());
+    EXPECT_EQ("\x44\x40\x40\x40\x40", Bstr("@@@@").toString());
+}
+
+TEST(SimpleValueTest, TextStringEncodings) {
+    EXPECT_EQ("\x60"s, Tstr("").toString());
+    EXPECT_EQ("\x61\x61"s, Tstr("a").toString());
+    EXPECT_EQ("\x61\x41"s, Tstr("A").toString());
+    EXPECT_EQ("\x64\x49\x45\x54\x46"s, Tstr("IETF").toString());
+    EXPECT_EQ("\x62\x22\x5c"s, Tstr("\"\\").toString());
+    EXPECT_EQ("\x62\xc3\xbc"s, Tstr("\xc3\xbc").toString());
+    EXPECT_EQ("\x63\xe6\xb0\xb4"s, Tstr("\xe6\xb0\xb4").toString());
+    EXPECT_EQ("\x64\xf0\x90\x85\x91"s, Tstr("\xf0\x90\x85\x91").toString());
+    EXPECT_EQ("\x64\x01\x02\x03\x04"s, Tstr("\x01\x02\x03\x04").toString());
+}
+
+TEST(IsIteratorPairOverTest, All) {
+    EXPECT_TRUE((
+            details::is_iterator_pair_over<pair<string::iterator, string::iterator>, char>::value));
+    EXPECT_TRUE((details::is_iterator_pair_over<pair<string::const_iterator, string::iterator>,
+                                                char>::value));
+    EXPECT_TRUE((details::is_iterator_pair_over<pair<string::iterator, string::const_iterator>,
+                                                char>::value));
+    EXPECT_TRUE((details::is_iterator_pair_over<pair<char*, char*>, char>::value));
+    EXPECT_TRUE((details::is_iterator_pair_over<pair<const char*, char*>, char>::value));
+    EXPECT_TRUE((details::is_iterator_pair_over<pair<char*, const char*>, char>::value));
+    EXPECT_FALSE((details::is_iterator_pair_over<pair<string::iterator, string::iterator>,
+                                                 uint8_t>::value));
+    EXPECT_FALSE((details::is_iterator_pair_over<pair<char*, char*>, uint8_t>::value));
+    EXPECT_TRUE((details::is_iterator_pair_over<
+                 pair<vector<uint8_t>::iterator, vector<uint8_t>::iterator>, uint8_t>::value));
+    EXPECT_TRUE((details::is_iterator_pair_over<
+                 pair<vector<uint8_t>::const_iterator, vector<uint8_t>::iterator>,
+                 uint8_t>::value));
+    EXPECT_TRUE((details::is_iterator_pair_over<
+                 pair<vector<uint8_t>::iterator, vector<uint8_t>::const_iterator>,
+                 uint8_t>::value));
+    EXPECT_TRUE((details::is_iterator_pair_over<pair<uint8_t*, uint8_t*>, uint8_t>::value));
+    EXPECT_TRUE((details::is_iterator_pair_over<pair<const uint8_t*, uint8_t*>, uint8_t>::value));
+    EXPECT_TRUE((details::is_iterator_pair_over<pair<uint8_t*, const uint8_t*>, uint8_t>::value));
+    EXPECT_FALSE((details::is_iterator_pair_over<
+                  pair<vector<uint8_t>::iterator, vector<uint8_t>::iterator>, char>::value));
+    EXPECT_FALSE((details::is_iterator_pair_over<pair<uint8_t*, const uint8_t*>, char>::value));
+}
+
+TEST(MakeEntryTest, Boolean) {
+    EXPECT_EQ("\xf4"s, details::makeItem(false)->toString());
+}
+
+TEST(MakeEntryTest, Integers) {
+    EXPECT_EQ("\x00"s, details::makeItem(static_cast<uint8_t>(0))->toString());
+    EXPECT_EQ("\x00"s, details::makeItem(static_cast<uint16_t>(0))->toString());
+    EXPECT_EQ("\x00"s, details::makeItem(static_cast<uint32_t>(0))->toString());
+    EXPECT_EQ("\x00"s, details::makeItem(static_cast<uint64_t>(0))->toString());
+    EXPECT_EQ("\x00"s, details::makeItem(static_cast<int8_t>(0))->toString());
+    EXPECT_EQ("\x00"s, details::makeItem(static_cast<int16_t>(0))->toString());
+    EXPECT_EQ("\x00"s, details::makeItem(static_cast<int32_t>(0))->toString());
+    EXPECT_EQ("\x00"s, details::makeItem(static_cast<int64_t>(0))->toString());
+    EXPECT_EQ("\x20"s, details::makeItem(static_cast<int8_t>(-1))->toString());
+    EXPECT_EQ("\x20"s, details::makeItem(static_cast<int16_t>(-1))->toString());
+    EXPECT_EQ("\x20"s, details::makeItem(static_cast<int32_t>(-1))->toString());
+    EXPECT_EQ("\x20"s, details::makeItem(static_cast<int64_t>(-1))->toString());
+
+    EXPECT_EQ("\x1b\xff\xff\xff\xff\xff\xff\xff\xff"s,
+              details::makeItem(static_cast<uint64_t>(std::numeric_limits<uint64_t>::max()))
+                      ->toString());
+}
+
+TEST(MakeEntryTest, StdStrings) {
+    string s1("hello");
+    const string s2("hello");
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(s1)->toString());  // copy of string
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s,
+              details::makeItem(s2)->toString());  // copy of const string
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s,
+              details::makeItem(std::move(s1))->toString());  // move string
+    EXPECT_EQ(0U, s1.size());                                 // Prove string was moved, not copied.
+}
+
+TEST(MakeEntryTest, StdStringViews) {
+    string_view s1("hello");
+    const string_view s2("hello");
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(s1)->toString());
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(s2)->toString());
+}
+
+TEST(MakeEntryTest, CStrings) {
+    char s1[] = "hello";
+    const char s2[] = "hello";
+    const char* s3 = "hello";
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(s1)->toString());
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(s2)->toString());
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(s3)->toString());
+}
+
+TEST(MakeEntryTest, StringIteratorPairs) {
+    // Use iterators from string to prove that "real" iterators work
+    string s1 = "hello"s;
+    pair<string::iterator, string::iterator> p1 = make_pair(s1.begin(), s1.end());
+
+    const pair<string::iterator, string::iterator> p2 = p1;
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(p1)->toString());
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(p2)->toString());
+
+    // Use char*s  as iterators
+    const char* s2 = "hello";
+    pair p3 = make_pair(s2, s2 + 5);
+    const pair p4 = p3;
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(p3)->toString());
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(p4)->toString());
+}
+
+TEST(MakeEntryTest, ByteStrings) {
+    vector<uint8_t> v1 = {0x00, 0x01, 0x02};
+    const vector<uint8_t> v2 = {0x00, 0x01, 0x02};
+    EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(v1)->toString());  // copy of vector
+    EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(v2)->toString());  // copy of const vector
+    EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(std::move(v1))->toString());  // move vector
+    EXPECT_EQ(0U, v1.size());  // Prove vector was moved, not copied.
+}
+
+TEST(MakeEntryTest, ByteStringIteratorPairs) {
+    using vec = vector<uint8_t>;
+    using iter = vec::iterator;
+    vec v1 = {0x00, 0x01, 0x02};
+    pair<iter, iter> p1 = make_pair(v1.begin(), v1.end());
+    const pair<iter, iter> p2 = make_pair(v1.begin(), v1.end());
+    EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(p1)->toString());
+    EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(p2)->toString());
+
+    // Use uint8_t*s as iterators
+    uint8_t v2[] = {0x00, 0x01, 0x02};
+    uint8_t* v3 = v2;
+    pair<uint8_t*, uint8_t*> p3 = make_pair(v2, v2 + 3);
+    const pair<uint8_t*, uint8_t*> p4 = make_pair(v2, v2 + 3);
+    pair<uint8_t*, uint8_t*> p5 = make_pair(v3, v3 + 3);
+    const pair<uint8_t*, uint8_t*> p6 = make_pair(v3, v3 + 3);
+    EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(p3)->toString());
+    EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(p4)->toString());
+    EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(p5)->toString());
+    EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(p6)->toString());
+}
+
+TEST(MakeEntryTest, ByteStringBuffers) {
+    uint8_t v1[] = {0x00, 0x01, 0x02};
+    EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(make_pair(v1, 3))->toString());
+}
+
+TEST(MakeEntryTest, ItemPointer) {
+    Uint* p1 = new Uint(0);
+    EXPECT_EQ("\x00"s, details::makeItem(p1)->toString());
+    EXPECT_EQ("\x60"s, details::makeItem(new Tstr(string()))->toString());
+}
+
+TEST(MakeEntryTest, ItemReference) {
+    Tstr str("hello"s);
+    Tstr& strRef = str;
+    const Tstr& strConstRef = str;
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(str)->toString());
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(strRef)->toString());
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(strConstRef)->toString());
+    EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(std::move(str))->toString());
+    EXPECT_EQ("\x60"s, details::makeItem(str)->toString());  // Prove that it moved
+
+    EXPECT_EQ("\x00"s, details::makeItem(Uint(0))->toString());
+
+    EXPECT_EQ("\x43\x00\x01\x02"s,
+              details::makeItem(Bstr(vector<uint8_t>{0x00, 0x01, 0x02}))->toString());
+
+    EXPECT_EQ("\x80"s, details::makeItem(Array())->toString());
+    EXPECT_EQ("\xa0"s, details::makeItem(Map())->toString());
+}
+
+TEST(CompoundValueTest, ArrayOfInts) {
+    EXPECT_EQ("\x80"s, Array().toString());
+    Array(Uint(0)).toString();
+
+    EXPECT_EQ("\x81\x00"s, Array(Uint(0U)).toString());
+    EXPECT_EQ("\x82\x00\x01"s, Array(Uint(0), Uint(1)).toString());
+    EXPECT_EQ("\x83\x00\x01\x38\x62"s, Array(Uint(0), Uint(1), Nint(-99)).toString());
+
+    EXPECT_EQ("\x81\x00"s, Array(0).toString());
+    EXPECT_EQ("\x82\x00\x01"s, Array(0, 1).toString());
+    EXPECT_EQ("\x83\x00\x01\x38\x62"s, Array(0, 1, -99).toString());
+}
+
+TEST(CompoundValueTest, MapOfInts) {
+    EXPECT_EQ("\xA0"s, Map().toString());
+    EXPECT_EQ("\xA1\x00\x01"s, Map(Uint(0), Uint(1)).toString());
+    // Maps with an odd number of arguments will fail to compile.  Uncomment the next lines to test.
+    // EXPECT_EQ("\xA1\x00"s, Map(Int(0)).toString());
+    // EXPECT_EQ("\xA1\x00\x01\x02"s, Map(Int(0), Int(1), Int(2)).toString());
+}
+
+TEST(CompoundValueTest, MixedArray) {
+    vector<uint8_t> vec = {3, 2, 1};
+    EXPECT_EQ("\x84\x01\x20\x43\x03\x02\x01\x65\x68\x65\x6C\x6C\x6F"s,
+              Array(Uint(1), Nint(-1), Bstr(vec), Tstr("hello")).toString());
+
+    EXPECT_EQ("\x84\x01\x20\x43\x03\x02\x01\x65\x68\x65\x6C\x6C\x6F"s,
+              Array(1, -1, vec, "hello").toString());
+}
+
+TEST(CompoundValueTest, MixedMap) {
+    vector<uint8_t> vec = {3, 2, 1};
+    EXPECT_EQ("\xA2\x01\x20\x43\x03\x02\x01\x65\x68\x65\x6C\x6C\x6F"s,
+              Map(Uint(1), Nint(-1), Bstr(vec), Tstr("hello")).toString());
+
+    EXPECT_EQ("\xA2\x01\x20\x43\x03\x02\x01\x65\x68\x65\x6C\x6C\x6F"s,
+              Map(1, -1, vec, "hello").toString());
+}
+
+TEST(CompoundValueTest, NestedStructures) {
+    vector<uint8_t> vec = {3, 2, 1};
+
+    string expectedEncoding =
+            "\xA2\x66\x4F\x75\x74\x65\x72\x31\x82\xA2\x66\x49\x6E\x6E\x65\x72\x31\x18\x63\x66\x49"
+            "\x6E"
+            "\x6E\x65\x72\x32\x43\x03\x02\x01\x63\x66\x6F\x6F\x66\x4F\x75\x74\x65\x72\x32\x0A"s;
+
+    // Do it with explicitly-created Items
+    EXPECT_EQ(expectedEncoding,
+              Map(Tstr("Outer1"),
+                  Array(  //
+                          Map(Tstr("Inner1"), Uint(99), Tstr("Inner2"), Bstr(vec)), Tstr("foo")),
+                  Tstr("Outer2"),  //
+                  Uint(10))
+                      .toString());
+    EXPECT_EQ(3U, vec.size());
+
+    // Now just use convertible types
+    EXPECT_EQ(expectedEncoding, Map("Outer1",
+                                    Array(Map("Inner1", 99,  //
+                                              "Inner2", vec),
+                                          "foo"),
+                                    "Outer2", 10)
+                                        .toString());
+    EXPECT_EQ(3U, vec.size());
+
+    // Finally, do it with the .add() method.  This is slightly less efficient, but has the
+    // advantage you can build a structure up incrementally, or somewhat fluently if you like.
+    // First, fluently.
+    EXPECT_EQ(expectedEncoding, Map().add("Outer1", Array().add(Map()  //
+                                                                        .add("Inner1", 99)
+                                                                        .add("Inner2", vec))
+                                                            .add("foo"))
+                                        .add("Outer2", 10)
+                                        .toString());
+    EXPECT_EQ(3U, vec.size());
+
+    // Next, more incrementally
+    Array arr;
+    arr.add(Map()  //
+                    .add("Inner1", 99)
+                    .add("Inner2", vec))
+            .add("foo");
+    EXPECT_EQ(3U, vec.size());
+
+    Map m;
+    m.add("Outer1", std::move(arr));  // Moving is necessary; Map and Array cannot be copied.
+    m.add("Outer2", 10);
+    auto s = m.toString();
+    EXPECT_EQ(expectedEncoding, s);
+}
+
+TEST(EncodingMethodsTest, AllVariants) {
+    Map map;
+    map.add("key1", Array().add(Map()  //
+                                        .add("key_a", 9999999)
+                                        .add("key_b", std::vector<uint8_t>{0x01, 0x02, 0x03})
+                                        .add("key_c", std::numeric_limits<uint64_t>::max())
+                                        .add("key_d", std::numeric_limits<int16_t>::min()))
+                            .add("foo"))
+            .add("key2", true);
+
+    std::vector<uint8_t> buf;
+    buf.resize(map.encodedSize());
+
+    EXPECT_EQ(buf.data() + buf.size(), map.encode(buf.data(), buf.data() + buf.size()));
+
+    EXPECT_EQ(buf, map.encode());
+
+    std::vector<uint8_t> buf2;
+    map.encode(std::back_inserter(buf2));
+    EXPECT_EQ(buf, buf2);
+
+    auto iter = buf.begin();
+    map.encode([&](uint8_t c) { EXPECT_EQ(c, *iter++); });
+}
+
+TEST(EncodingMethodsTest, UintWithTooShortBuf) {
+    Uint val(100000);
+    vector<uint8_t> buf(val.encodedSize() - 1);
+    EXPECT_EQ(nullptr, val.encode(buf.data(), buf.data() + buf.size()));
+}
+
+TEST(EncodingMethodsTest, TstrWithTooShortBuf) {
+    Tstr val("01234567890123456789012345"s);
+    vector<uint8_t> buf(1);
+    EXPECT_EQ(nullptr, val.encode(buf.data(), buf.data() + buf.size()));
+
+    buf.resize(val.encodedSize() - 1);
+    EXPECT_EQ(nullptr, val.encode(buf.data(), buf.data() + buf.size()));
+}
+
+TEST(EncodingMethodsTest, BstrWithTooShortBuf) {
+    Bstr val("01234567890123456789012345"s);
+    vector<uint8_t> buf(1);
+    EXPECT_EQ(nullptr, val.encode(buf.data(), buf.data() + buf.size()));
+
+    buf.resize(val.encodedSize() - 1);
+    EXPECT_EQ(nullptr, val.encode(buf.data(), buf.data() + buf.size()));
+}
+
+TEST(EncodingMethodsTest, ArrayWithTooShortBuf) {
+    Array val("a", 5, -100);
+
+    std::vector<uint8_t> buf(val.encodedSize() - 1);
+    EXPECT_EQ(nullptr, val.encode(buf.data(), buf.data() + buf.size()));
+}
+
+TEST(EncodingMethodsTest, MapWithTooShortBuf) {
+    Map map;
+    map.add("key1", Array().add(Map()  //
+                                        .add("key_a", 99)
+                                        .add("key_b", std::vector<uint8_t>{0x01, 0x02, 0x03}))
+                            .add("foo"))
+            .add("key2", true);
+
+    std::vector<uint8_t> buf(map.encodedSize() - 1);
+    EXPECT_EQ(nullptr, map.encode(buf.data(), buf.data() + buf.size()));
+}
+
+TEST(EqualityTest, Uint) {
+    Uint val(99);
+    EXPECT_EQ(val, Uint(99));
+
+    EXPECT_NE(val, Uint(98));
+    EXPECT_NE(val, Nint(-1));
+    EXPECT_NE(val, Tstr("99"));
+    EXPECT_NE(val, Bstr("99"));
+    EXPECT_NE(val, Bool(false));
+    EXPECT_NE(val, Array(99, 1));
+    EXPECT_NE(val, Map(99, 1));
+}
+
+TEST(EqualityTest, Nint) {
+    Nint val(-1);
+    EXPECT_EQ(val, Nint(-1));
+
+    EXPECT_NE(val, Uint(99));
+    EXPECT_NE(val, Nint(-4));
+    EXPECT_NE(val, Tstr("99"));
+    EXPECT_NE(val, Bstr("99"));
+    EXPECT_NE(val, Bool(false));
+    EXPECT_NE(val, Array(99));
+    EXPECT_NE(val, Map(99, 1));
+}
+
+TEST(EqualityTest, Tstr) {
+    Tstr val("99");
+    EXPECT_EQ(val, Tstr("99"));
+
+    EXPECT_NE(val, Uint(99));
+    EXPECT_NE(val, Nint(-1));
+    EXPECT_NE(val, Nint(-4));
+    EXPECT_NE(val, Tstr("98"));
+    EXPECT_NE(val, Bstr("99"));
+    EXPECT_NE(val, Bool(false));
+    EXPECT_NE(val, Array(99, 1));
+    EXPECT_NE(val, Map(99, 1));
+}
+
+TEST(EqualityTest, Bstr) {
+    Bstr val("99");
+    EXPECT_EQ(val, Bstr("99"));
+
+    EXPECT_NE(val, Uint(99));
+    EXPECT_NE(val, Nint(-1));
+    EXPECT_NE(val, Nint(-4));
+    EXPECT_NE(val, Tstr("99"));
+    EXPECT_NE(val, Bstr("98"));
+    EXPECT_NE(val, Bool(false));
+    EXPECT_NE(val, Array(99, 1));
+    EXPECT_NE(val, Map(99, 1));
+}
+
+TEST(EqualityTest, Bool) {
+    Bool val(false);
+    EXPECT_EQ(val, Bool(false));
+
+    EXPECT_NE(val, Uint(99));
+    EXPECT_NE(val, Nint(-1));
+    EXPECT_NE(val, Nint(-4));
+    EXPECT_NE(val, Tstr("99"));
+    EXPECT_NE(val, Bstr("98"));
+    EXPECT_NE(val, Bool(true));
+    EXPECT_NE(val, Array(99, 1));
+    EXPECT_NE(val, Map(99, 1));
+}
+
+TEST(EqualityTest, Array) {
+    Array val(99, 1);
+    EXPECT_EQ(val, Array(99, 1));
+
+    EXPECT_NE(val, Uint(99));
+    EXPECT_NE(val, Nint(-1));
+    EXPECT_NE(val, Nint(-4));
+    EXPECT_NE(val, Tstr("99"));
+    EXPECT_NE(val, Bstr("98"));
+    EXPECT_NE(val, Bool(true));
+    EXPECT_NE(val, Array(99, 2));
+    EXPECT_NE(val, Array(98, 1));
+    EXPECT_NE(val, Array(99, 1, 2));
+    EXPECT_NE(val, Map(99, 1));
+}
+
+TEST(EqualityTest, Map) {
+    Map val(99, 1);
+    EXPECT_EQ(val, Map(99, 1));
+
+    EXPECT_NE(val, Uint(99));
+    EXPECT_NE(val, Nint(-1));
+    EXPECT_NE(val, Nint(-4));
+    EXPECT_NE(val, Tstr("99"));
+    EXPECT_NE(val, Bstr("98"));
+    EXPECT_NE(val, Bool(true));
+    EXPECT_NE(val, Array(99, 1));
+    EXPECT_NE(val, Map(99, 2));
+    EXPECT_NE(val, Map(99, 1, 99, 2));
+}
+
+TEST(ConvertTest, Uint) {
+    unique_ptr<Item> item = details::makeItem(10);
+
+    EXPECT_EQ(UINT, item->type());
+    EXPECT_NE(nullptr, item->asInt());
+    EXPECT_NE(nullptr, item->asUint());
+    EXPECT_EQ(nullptr, item->asNint());
+    EXPECT_EQ(nullptr, item->asTstr());
+    EXPECT_EQ(nullptr, item->asBstr());
+    EXPECT_EQ(nullptr, item->asSimple());
+    EXPECT_EQ(nullptr, item->asMap());
+    EXPECT_EQ(nullptr, item->asArray());
+
+    EXPECT_EQ(10, item->asInt()->value());
+    EXPECT_EQ(10, item->asUint()->value());
+}
+
+TEST(ConvertTest, Nint) {
+    unique_ptr<Item> item = details::makeItem(-10);
+
+    EXPECT_EQ(NINT, item->type());
+    EXPECT_NE(nullptr, item->asInt());
+    EXPECT_EQ(nullptr, item->asUint());
+    EXPECT_NE(nullptr, item->asNint());
+    EXPECT_EQ(nullptr, item->asTstr());
+    EXPECT_EQ(nullptr, item->asBstr());
+    EXPECT_EQ(nullptr, item->asSimple());
+    EXPECT_EQ(nullptr, item->asMap());
+    EXPECT_EQ(nullptr, item->asArray());
+
+    EXPECT_EQ(-10, item->asInt()->value());
+    EXPECT_EQ(-10, item->asNint()->value());
+}
+
+TEST(ConvertTest, Tstr) {
+    unique_ptr<Item> item = details::makeItem("hello");
+
+    EXPECT_EQ(TSTR, item->type());
+    EXPECT_EQ(nullptr, item->asInt());
+    EXPECT_EQ(nullptr, item->asUint());
+    EXPECT_EQ(nullptr, item->asNint());
+    EXPECT_NE(nullptr, item->asTstr());
+    EXPECT_EQ(nullptr, item->asBstr());
+    EXPECT_EQ(nullptr, item->asSimple());
+    EXPECT_EQ(nullptr, item->asMap());
+    EXPECT_EQ(nullptr, item->asArray());
+
+    EXPECT_EQ("hello"s, item->asTstr()->value());
+}
+
+TEST(ConvertTest, Bstr) {
+    vector<uint8_t> vec{0x23, 0x24, 0x22};
+    unique_ptr<Item> item = details::makeItem(vec);
+
+    EXPECT_EQ(BSTR, item->type());
+    EXPECT_EQ(nullptr, item->asInt());
+    EXPECT_EQ(nullptr, item->asUint());
+    EXPECT_EQ(nullptr, item->asNint());
+    EXPECT_EQ(nullptr, item->asTstr());
+    EXPECT_NE(nullptr, item->asBstr());
+    EXPECT_EQ(nullptr, item->asSimple());
+    EXPECT_EQ(nullptr, item->asMap());
+    EXPECT_EQ(nullptr, item->asArray());
+
+    EXPECT_EQ(vec, item->asBstr()->value());
+}
+
+TEST(ConvertTest, Bool) {
+    unique_ptr<Item> item = details::makeItem(false);
+
+    EXPECT_EQ(SIMPLE, item->type());
+    EXPECT_EQ(nullptr, item->asInt());
+    EXPECT_EQ(nullptr, item->asUint());
+    EXPECT_EQ(nullptr, item->asNint());
+    EXPECT_EQ(nullptr, item->asTstr());
+    EXPECT_EQ(nullptr, item->asBstr());
+    EXPECT_NE(nullptr, item->asSimple());
+    EXPECT_EQ(nullptr, item->asMap());
+    EXPECT_EQ(nullptr, item->asArray());
+
+    EXPECT_EQ(BOOLEAN, item->asSimple()->simpleType());
+    EXPECT_NE(nullptr, item->asSimple()->asBool());
+
+    EXPECT_FALSE(item->asSimple()->asBool()->value());
+}
+
+TEST(ConvertTest, Map) {
+    unique_ptr<Item> item(new Map);
+
+    EXPECT_EQ(MAP, item->type());
+    EXPECT_EQ(nullptr, item->asInt());
+    EXPECT_EQ(nullptr, item->asUint());
+    EXPECT_EQ(nullptr, item->asNint());
+    EXPECT_EQ(nullptr, item->asTstr());
+    EXPECT_EQ(nullptr, item->asBstr());
+    EXPECT_EQ(nullptr, item->asSimple());
+    EXPECT_NE(nullptr, item->asMap());
+    EXPECT_EQ(nullptr, item->asArray());
+
+    EXPECT_EQ(0U, item->asMap()->size());
+}
+
+TEST(ConvertTest, Array) {
+    unique_ptr<Item> item(new Array);
+
+    EXPECT_EQ(ARRAY, item->type());
+    EXPECT_EQ(nullptr, item->asInt());
+    EXPECT_EQ(nullptr, item->asUint());
+    EXPECT_EQ(nullptr, item->asNint());
+    EXPECT_EQ(nullptr, item->asTstr());
+    EXPECT_EQ(nullptr, item->asBstr());
+    EXPECT_EQ(nullptr, item->asSimple());
+    EXPECT_EQ(nullptr, item->asMap());
+    EXPECT_NE(nullptr, item->asArray());
+
+    EXPECT_EQ(0U, item->asArray()->size());
+}
+
+class MockParseClient : public ParseClient {
+  public:
+    MOCK_METHOD4(item, ParseClient*(std::unique_ptr<Item>& item, const uint8_t* hdrBegin,
+                                    const uint8_t* valueBegin, const uint8_t* end));
+    MOCK_METHOD4(itemEnd, ParseClient*(std::unique_ptr<Item>& item, const uint8_t* hdrBegin,
+                                       const uint8_t* valueBegin, const uint8_t* end));
+    MOCK_METHOD2(error, void(const uint8_t* position, const std::string& errorMessage));
+};
+
+MATCHER_P(IsType, value, std::string("Type ") + (negation ? "doesn't match" : "matches")) {
+    return arg->type() == value;
+}
+
+MATCHER_P(MatchesItem, value, "") {
+    return arg && *arg == value;
+}
+
+MATCHER_P(IsArrayOfSize, value, "") {
+    return arg->type() == ARRAY && arg->asArray()->size() == value;
+}
+
+MATCHER_P(IsMapOfSize, value, "") {
+    return arg->type() == MAP && arg->asMap()->size() == value;
+}
+
+TEST(StreamParseTest, Uint) {
+    MockParseClient mpc;
+
+    Uint val(100);
+    auto encoded = val.encode();
+    uint8_t* encBegin = encoded.data();
+    uint8_t* encEnd = encoded.data() + encoded.size();
+
+    EXPECT_CALL(mpc, item(MatchesItem(val), encBegin, encEnd, encEnd)).WillOnce(Return(&mpc));
+    EXPECT_CALL(mpc, itemEnd(_, _, _, _)).Times(0);
+    EXPECT_CALL(mpc, error(_, _)).Times(0);
+
+    parse(encoded.data(), encoded.data() + encoded.size(), &mpc);
+}
+
+TEST(StreamParseTest, Nint) {
+    MockParseClient mpc;
+
+    Nint val(-10);
+    auto encoded = val.encode();
+    uint8_t* encBegin = encoded.data();
+    uint8_t* encEnd = encoded.data() + encoded.size();
+
+    EXPECT_CALL(mpc, item(MatchesItem(val), encBegin, encEnd, encEnd)).WillOnce(Return(&mpc));
+
+    EXPECT_CALL(mpc, itemEnd(_, _, _, _)).Times(0);
+    EXPECT_CALL(mpc, error(_, _)).Times(0);
+
+    parse(encoded.data(), encoded.data() + encoded.size(), &mpc);
+}
+
+TEST(StreamParseTest, Bool) {
+    MockParseClient mpc;
+
+    Bool val(true);
+    auto encoded = val.encode();
+    uint8_t* encBegin = encoded.data();
+    uint8_t* encEnd = encoded.data() + encoded.size();
+
+    EXPECT_CALL(mpc, item(MatchesItem(val), encBegin, encEnd, encEnd)).WillOnce(Return(&mpc));
+    EXPECT_CALL(mpc, itemEnd(_, _, _, _)).Times(0);
+    EXPECT_CALL(mpc, error(_, _)).Times(0);
+
+    parse(encoded.data(), encoded.data() + encoded.size(), &mpc);
+}
+
+TEST(StreamParseTest, Tstr) {
+    MockParseClient mpc;
+
+    Tstr val("Hello");
+    auto encoded = val.encode();
+    uint8_t* encBegin = encoded.data();
+    uint8_t* encEnd = encoded.data() + encoded.size();
+
+    EXPECT_CALL(mpc, item(MatchesItem(val), encBegin, encBegin + 1, encEnd)).WillOnce(Return(&mpc));
+    EXPECT_CALL(mpc, itemEnd(_, _, _, _)).Times(0);
+    EXPECT_CALL(mpc, error(_, _)).Times(0);
+
+    parse(encoded.data(), encoded.data() + encoded.size(), &mpc);
+}
+
+TEST(StreamParseTest, Bstr) {
+    MockParseClient mpc;
+
+    Bstr val("Hello");
+    auto encoded = val.encode();
+    uint8_t* encBegin = encoded.data();
+    uint8_t* encEnd = encoded.data() + encoded.size();
+
+    EXPECT_CALL(mpc, item(MatchesItem(val), encBegin, encBegin + 1, encEnd)).WillOnce(Return(&mpc));
+    EXPECT_CALL(mpc, itemEnd(_, _, _, _)).Times(0);
+    EXPECT_CALL(mpc, error(_, _)).Times(0);
+
+    parse(encoded.data(), encoded.data() + encoded.size(), &mpc);
+}
+
+TEST(StreamParseTest, Array) {
+    MockParseClient mpc;
+
+    Array val("Hello", 4, Array(-9, "Goodbye"), std::numeric_limits<uint64_t>::max());
+    ASSERT_NE(val[2]->asArray(), nullptr);
+    const Array& interior = *(val[2]->asArray());
+    auto encoded = val.encode();
+    uint8_t* encBegin = encoded.data();
+    uint8_t* encEnd = encoded.data() + encoded.size();
+
+    {
+        InSequence s;
+        const uint8_t* pos = encBegin;
+        EXPECT_CALL(mpc, item(IsArrayOfSize(val.size()), pos, pos + 1, pos + 1))
+                .WillOnce(Return(&mpc));
+        ++pos;
+        EXPECT_CALL(mpc, item(MatchesItem(ByRef(*val[0])), pos, pos + 1, pos + 6))
+                .WillOnce(Return(&mpc));
+        pos += 6;
+        EXPECT_CALL(mpc, item(MatchesItem(ByRef(*val[1])), pos, pos + 1, pos + 1))
+                .WillOnce(Return(&mpc));
+        ++pos;
+        const uint8_t* innerArrayBegin = pos;
+        EXPECT_CALL(mpc, item(IsArrayOfSize(interior.size()), pos, pos + 1, pos + 1))
+                .WillOnce(Return(&mpc));
+        ++pos;
+        EXPECT_CALL(mpc, item(MatchesItem(ByRef(*interior[0])), pos, pos + 1, pos + 1))
+                .WillOnce(Return(&mpc));
+        ++pos;
+        EXPECT_CALL(mpc, item(MatchesItem(ByRef(*interior[1])), pos, pos + 1, pos + 8))
+                .WillOnce(Return(&mpc));
+        pos += 8;
+        EXPECT_CALL(mpc, itemEnd(IsArrayOfSize(interior.size()), innerArrayBegin,
+                                 innerArrayBegin + 1, pos))
+                .WillOnce(Return(&mpc));
+        EXPECT_CALL(mpc, item(MatchesItem(ByRef(*val[3])), pos, pos + 9, pos + 9))
+                .WillOnce(Return(&mpc));
+        EXPECT_CALL(mpc, itemEnd(IsArrayOfSize(val.size()), encBegin, encBegin + 1, encEnd))
+                .WillOnce(Return(&mpc));
+    }
+
+    EXPECT_CALL(mpc, error(_, _))  //
+            .Times(0);
+
+    parse(encoded.data(), encoded.data() + encoded.size(), &mpc);
+}
+
+TEST(StreamParseTest, Map) {
+    MockParseClient mpc;
+
+    Map val("Hello", 4, Array(-9, "Goodbye"), std::numeric_limits<uint64_t>::max());
+    ASSERT_NE(val[1].first->asArray(), nullptr);
+    const Array& interior = *(val[1].first->asArray());
+    auto encoded = val.encode();
+    uint8_t* encBegin = encoded.data();
+    uint8_t* encEnd = encoded.data() + encoded.size();
+
+    {
+        InSequence s;
+        const uint8_t* pos = encBegin;
+        EXPECT_CALL(mpc, item(_, pos, pos + 1, pos + 1)).WillOnce(Return(&mpc));
+        ++pos;
+        EXPECT_CALL(mpc, item(MatchesItem(ByRef(*val[0].first)), pos, pos + 1, pos + 6))
+                .WillOnce(Return(&mpc));
+        pos += 6;
+        EXPECT_CALL(mpc, item(MatchesItem(ByRef(*val[0].second)), pos, pos + 1, pos + 1))
+                .WillOnce(Return(&mpc));
+        ++pos;
+        const uint8_t* innerArrayBegin = pos;
+        EXPECT_CALL(mpc, item(IsArrayOfSize(interior.size()), pos, pos + 1, pos + 1))
+                .WillOnce(Return(&mpc));
+        ++pos;
+        EXPECT_CALL(mpc, item(MatchesItem(ByRef(*interior[0])), pos, pos + 1, pos + 1))
+                .WillOnce(Return(&mpc));
+        ++pos;
+        EXPECT_CALL(mpc, item(MatchesItem(ByRef(*interior[1])), pos, pos + 1, pos + 8))
+                .WillOnce(Return(&mpc));
+        pos += 8;
+        EXPECT_CALL(mpc, itemEnd(IsArrayOfSize(interior.size()), innerArrayBegin,
+                                 innerArrayBegin + 1, pos))
+                .WillOnce(Return(&mpc));
+        EXPECT_CALL(mpc, item(MatchesItem(ByRef(*val[1].second)), pos, pos + 9, pos + 9))
+                .WillOnce(Return(&mpc));
+        EXPECT_CALL(mpc, itemEnd(IsMapOfSize(val.size()), encBegin, encBegin + 1, encEnd))
+                .WillOnce(Return(&mpc));
+    }
+
+    EXPECT_CALL(mpc, error(_, _))  //
+            .Times(0);
+
+    parse(encoded.data(), encoded.data() + encoded.size(), &mpc);
+}
+
+TEST(StreamParseTest, Semantic) {
+    MockParseClient mpc;
+
+    vector<uint8_t> encoded;
+    auto iter = back_inserter(encoded);
+    encodeHeader(SEMANTIC, 0, iter);
+    Uint(999).encode(iter);
+
+    EXPECT_CALL(mpc, item(_, _, _, _)).Times(0);
+    EXPECT_CALL(mpc, itemEnd(_, _, _, _)).Times(0);
+    EXPECT_CALL(mpc, error(encoded.data(), "Semantic tags not supported"));
+
+    parse(encoded.data(), encoded.data() + encoded.size(), &mpc);
+}
+
+TEST(FullParserTest, Uint) {
+    Uint val(10);
+
+    auto [item, pos, message] = parse(val.encode());
+    EXPECT_THAT(item, MatchesItem(val));
+}
+
+TEST(FullParserTest, Nint) {
+    Nint val(-10);
+
+    auto [item, pos, message] = parse(val.encode());
+    EXPECT_THAT(item, MatchesItem(val));
+
+    vector<uint8_t> minNint = {0x3B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+    std::tie(item, pos, message) = parse(minNint);
+    EXPECT_THAT(item, NotNull());
+    EXPECT_EQ(item->asNint()->value(), std::numeric_limits<int64_t>::min());
+}
+
+TEST(FullParserTest, NintOutOfRange) {
+    vector<uint8_t> outOfRangeNint = {0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+    auto [item, pos, message] = parse(outOfRangeNint);
+    EXPECT_THAT(item, IsNull());
+    EXPECT_EQ(pos, outOfRangeNint.data());
+    EXPECT_EQ(message, "NINT values that don't fit in int64_t are not supported.");
+}
+
+TEST(FullParserTest, Tstr) {
+    Tstr val("Hello");
+
+    auto [item, pos, message] = parse(val.encode());
+    EXPECT_THAT(item, MatchesItem(val));
+}
+
+TEST(FullParserTest, Bstr) {
+    Bstr val("\x00\x01\0x02"s);
+
+    auto [item, pos, message] = parse(val.encode());
+    EXPECT_THAT(item, MatchesItem(val));
+}
+
+TEST(FullParserTest, Array) {
+    Array val("hello", -4, 3);
+
+    auto encoded = val.encode();
+    auto [item, pos, message] = parse(encoded);
+    EXPECT_THAT(item, MatchesItem(ByRef(val)));
+    EXPECT_EQ(pos, encoded.data() + encoded.size());
+    EXPECT_EQ("", message);
+
+    // We've already checked it all, but walk it just for fun.
+    ASSERT_NE(nullptr, item->asArray());
+    const Array& arr = *(item->asArray());
+    ASSERT_EQ(arr[0]->type(), TSTR);
+    EXPECT_EQ(arr[0]->asTstr()->value(), "hello");
+}
+
+TEST(FullParserTest, Map) {
+    Map val("hello", -4, 3, Bstr("hi"));
+
+    auto [item, pos, message] = parse(val.encode());
+    EXPECT_THAT(item, MatchesItem(ByRef(val)));
+}
+
+TEST(FullParserTest, Complex) {
+    vector<uint8_t> vec = {0x01, 0x02, 0x08, 0x03};
+    Map val("Outer1",
+            Array(Map("Inner1", 99,  //
+                      "Inner2", vec),
+                  "foo"),
+            "Outer2", 10);
+
+    std::unique_ptr<Item> item;
+    const uint8_t* pos;
+    std::string message;
+    std::tie(item, pos, message) = parse(val.encode());
+    EXPECT_THAT(item, MatchesItem(ByRef(val)));
+}
+
+TEST(FullParserTest, IncompleteUint) {
+    Uint val(1000);
+
+    auto encoding = val.encode();
+    auto [item, pos, message] = parse(encoding.data(), encoding.size() - 1);
+    EXPECT_EQ(nullptr, item.get());
+    EXPECT_EQ(encoding.data(), pos);
+    EXPECT_EQ("Need 2 byte(s) for length field, have 1.", message);
+}
+
+TEST(FullParserTest, IncompleteString) {
+    Tstr val("hello");
+
+    auto encoding = val.encode();
+    auto [item, pos, message] = parse(encoding.data(), encoding.size() - 2);
+    EXPECT_EQ(nullptr, item.get());
+    EXPECT_EQ(encoding.data(), pos);
+    EXPECT_EQ("Need 5 byte(s) for text string, have 3.", message);
+}
+
+TEST(FullParserTest, ArrayWithInsufficientEntries) {
+    Array val(1, 2, 3, 4);
+
+    auto encoding = val.encode();
+    auto [item, pos, message] = parse(encoding.data(), encoding.size() - 1);
+    EXPECT_EQ(nullptr, item.get());
+    EXPECT_EQ(encoding.data(), pos);
+    EXPECT_EQ("Not enough entries for array.", message);
+}
+
+TEST(FullParserTest, ArrayWithTruncatedEntry) {
+    Array val(1, 2, 3, 400000);
+
+    auto encoding = val.encode();
+    auto [item, pos, message] = parse(encoding.data(), encoding.size() - 1);
+    EXPECT_EQ(nullptr, item.get());
+    EXPECT_EQ(encoding.data() + encoding.size() - 5, pos);
+    EXPECT_EQ("Need 4 byte(s) for length field, have 3.", message);
+}
+
+TEST(FullParserTest, MapWithTruncatedEntry) {
+    Map val(1, 2, 300000, 4);
+
+    auto encoding = val.encode();
+    auto [item, pos, message] = parse(encoding.data(), encoding.size() - 2);
+    EXPECT_EQ(nullptr, item.get());
+    EXPECT_EQ(encoding.data() + 3, pos);
+    EXPECT_EQ("Need 4 byte(s) for length field, have 3.", message);
+}
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/keymaster/4.0/support/Android.bp b/keymaster/4.0/support/Android.bp
index 2f40282..9c5fbab 100644
--- a/keymaster/4.0/support/Android.bp
+++ b/keymaster/4.0/support/Android.bp
@@ -27,11 +27,10 @@
         "authorization_set.cpp",
         "key_param_output.cpp",
         "keymaster_utils.cpp",
-        "Keymaster.cpp",
-        "Keymaster3.cpp",
-        "Keymaster4.cpp",
     ],
-    export_include_dirs: ["include"],
+    export_include_dirs: [
+        "include",
+    ],
     shared_libs: [
         "android.hardware.keymaster@3.0",
         "android.hardware.keymaster@4.0",
@@ -39,6 +38,5 @@
         "libcrypto",
         "libhardware",
         "libhidlbase",
-        "libutils",
-    ]
+    ],
 }
diff --git a/keymaster/4.0/support/Keymaster4.cpp b/keymaster/4.0/support/Keymaster4.cpp
deleted file mode 100644
index cc3d656..0000000
--- a/keymaster/4.0/support/Keymaster4.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <keymasterV4_0/Keymaster4.h>
-
-#include <android-base/logging.h>
-
-namespace android {
-namespace hardware {
-namespace keymaster {
-namespace V4_0 {
-namespace support {
-
-void Keymaster4::getVersionIfNeeded() {
-    if (haveVersion_) return;
-
-    auto rc =
-        dev_->getHardwareInfo([&](SecurityLevel securityLevel, const hidl_string& keymasterName,
-                                  const hidl_string& authorName) {
-            version_ = {keymasterName, authorName, 4 /* major version */, securityLevel,
-                        true /* supportsEc */};
-            haveVersion_ = true;
-        });
-
-    CHECK(rc.isOk()) << "Got error " << rc.description() << " trying to get hardware info";
-}
-
-}  // namespace support
-}  // namespace V4_0
-}  // namespace keymaster
-}  // namespace hardware
-}  // namespace android
diff --git a/keymaster/4.0/support/OWNERS b/keymaster/4.0/support/OWNERS
index 335660d..a9efe66 100644
--- a/keymaster/4.0/support/OWNERS
+++ b/keymaster/4.0/support/OWNERS
@@ -1,2 +1,3 @@
 jdanis@google.com
 swillden@google.com
+jbires@google.com
diff --git a/keymaster/4.0/support/attestation_record.cpp b/keymaster/4.0/support/attestation_record.cpp
index 000d46e..27e00c1 100644
--- a/keymaster/4.0/support/attestation_record.cpp
+++ b/keymaster/4.0/support/attestation_record.cpp
@@ -321,19 +321,20 @@
         LOG(ERROR) << AT << "Failed record parsing";
         return ErrorCode::UNKNOWN_ERROR;
     }
-    if (!record->tee_enforced) {
-        LOG(ERROR) << AT << "Failed hardware characteristic parsing";
+
+    KM_ROOT_OF_TRUST* root_of_trust = nullptr;
+    if (record->tee_enforced && record->tee_enforced->root_of_trust) {
+        root_of_trust = record->tee_enforced->root_of_trust;
+    } else if (record->software_enforced && record->software_enforced->root_of_trust) {
+        root_of_trust = record->software_enforced->root_of_trust;
+    } else {
+        LOG(ERROR) << AT << " Failed root of trust parsing";
         return ErrorCode::INVALID_ARGUMENT;
     }
-    if (!record->tee_enforced->root_of_trust) {
-        LOG(ERROR) << AT << "Failed root of trust parsing";
+    if (!root_of_trust->verified_boot_key) {
+        LOG(ERROR) << AT << " Failed verified boot key parsing";
         return ErrorCode::INVALID_ARGUMENT;
     }
-    if (!record->tee_enforced->root_of_trust->verified_boot_key) {
-        LOG(ERROR) << AT << "Failed verified boot key parsing";
-        return ErrorCode::INVALID_ARGUMENT;
-    }
-    KM_ROOT_OF_TRUST* root_of_trust = record->tee_enforced->root_of_trust;
 
     auto& vb_key = root_of_trust->verified_boot_key;
     verified_boot_key->resize(vb_key->length);
@@ -342,19 +343,19 @@
     *verified_boot_state = static_cast<keymaster_verified_boot_t>(
             ASN1_ENUMERATED_get(root_of_trust->verified_boot_state));
     if (!verified_boot_state) {
-        LOG(ERROR) << AT << "Failed verified boot state parsing";
+        LOG(ERROR) << AT << " Failed verified boot state parsing";
         return ErrorCode::INVALID_ARGUMENT;
     }
 
     *device_locked = root_of_trust->device_locked;
     if (!device_locked) {
-        LOG(ERROR) << AT << "Failed device locked parsing";
+        LOG(ERROR) << AT << " Failed device locked parsing";
         return ErrorCode::INVALID_ARGUMENT;
     }
 
     auto& vb_hash = root_of_trust->verified_boot_hash;
     if (!vb_hash) {
-        LOG(ERROR) << AT << "Failed verified boot hash parsing";
+        LOG(ERROR) << AT << " Failed verified boot hash parsing";
         return ErrorCode::INVALID_ARGUMENT;
     }
     verified_boot_hash->resize(vb_hash->length);
diff --git a/keymaster/4.0/support/authorization_set.cpp b/keymaster/4.0/support/authorization_set.cpp
index d6b50f5..a024ff9 100644
--- a/keymaster/4.0/support/authorization_set.cpp
+++ b/keymaster/4.0/support/authorization_set.cpp
@@ -25,7 +25,7 @@
 namespace keymaster {
 namespace V4_0 {
 
-inline bool keyParamLess(const KeyParameter& a, const KeyParameter& b) {
+bool keyParamLess(const KeyParameter& a, const KeyParameter& b) {
     if (a.tag != b.tag) return a.tag < b.tag;
     int retval;
     switch (typeFromTag(a.tag)) {
@@ -58,7 +58,7 @@
     return false;
 }
 
-inline bool keyParamEqual(const KeyParameter& a, const KeyParameter& b) {
+bool keyParamEqual(const KeyParameter& a, const KeyParameter& b) {
     if (a.tag != b.tag) return false;
 
     switch (typeFromTag(a.tag)) {
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
deleted file mode 100644
index dfd03ef..0000000
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- **
- ** Copyright 2017, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- **     http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
- */
-
-#ifndef HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_4_H_
-#define HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_4_H_
-
-#include "Keymaster.h"
-
-namespace android {
-namespace hardware {
-namespace keymaster {
-namespace V4_0 {
-namespace support {
-
-using android::sp;
-using IKeymaster4Device = ::android::hardware::keymaster::V4_0::IKeymasterDevice;
-
-class Keymaster4 : public Keymaster {
-   public:
-    using WrappedIKeymasterDevice = IKeymaster4Device;
-    Keymaster4(sp<IKeymasterDevice> km4_dev, const hidl_string& instanceName)
-        : Keymaster(IKeymaster4Device::descriptor, instanceName),
-          haveVersion_(false),
-          dev_(km4_dev) {}
-
-    const VersionResult& halVersion() const override {
-        const_cast<Keymaster4*>(this)->getVersionIfNeeded();
-        return version_;
-    }
-
-    Return<void> getHardwareInfo(getHardwareInfo_cb _hidl_cb) override {
-        return dev_->getHardwareInfo(_hidl_cb);
-    }
-
-    Return<void> getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) override {
-        return dev_->getHmacSharingParameters(_hidl_cb);
-    }
-
-    Return<void> computeSharedHmac(const hidl_vec<HmacSharingParameters>& params,
-                                   computeSharedHmac_cb _hidl_cb) override {
-        return dev_->computeSharedHmac(params, _hidl_cb);
-    }
-
-    Return<void> verifyAuthorization(uint64_t operationHandle, const hidl_vec<KeyParameter>& params,
-                                     const HardwareAuthToken& authToken,
-                                     verifyAuthorization_cb _hidl_cb) override {
-        return dev_->verifyAuthorization(operationHandle, params, authToken, _hidl_cb);
-    }
-
-    Return<ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override {
-        return dev_->addRngEntropy(data);
-    }
-
-    Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
-                             generateKey_cb _hidl_cb) override {
-        return dev_->generateKey(keyParams, _hidl_cb);
-    }
-
-    Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
-                                       const hidl_vec<uint8_t>& clientId,
-                                       const hidl_vec<uint8_t>& appData,
-                                       getKeyCharacteristics_cb _hidl_cb) override {
-        return dev_->getKeyCharacteristics(keyBlob, clientId, appData, _hidl_cb);
-    }
-
-    Return<void> importKey(const hidl_vec<KeyParameter>& params, KeyFormat keyFormat,
-                           const hidl_vec<uint8_t>& keyData, importKey_cb _hidl_cb) override {
-        return dev_->importKey(params, keyFormat, keyData, _hidl_cb);
-    }
-
-    Return<void> importWrappedKey(const hidl_vec<uint8_t>& wrappedKeyData,
-                                  const hidl_vec<uint8_t>& wrappingKeyBlob,
-                                  const hidl_vec<uint8_t>& maskingKey,
-                                  const hidl_vec<KeyParameter>& unwrappingParams,
-                                  uint64_t passwordSid, uint64_t biometricSid,
-                                  importWrappedKey_cb _hidl_cb) {
-        return dev_->importWrappedKey(wrappedKeyData, wrappingKeyBlob, maskingKey, unwrappingParams,
-                                      passwordSid, biometricSid, _hidl_cb);
-    }
-
-    Return<void> exportKey(KeyFormat exportFormat, const hidl_vec<uint8_t>& keyBlob,
-                           const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
-                           exportKey_cb _hidl_cb) override {
-        return dev_->exportKey(exportFormat, keyBlob, clientId, appData, _hidl_cb);
-    }
-
-    Return<void> attestKey(const hidl_vec<uint8_t>& keyToAttest,
-                           const hidl_vec<KeyParameter>& attestParams,
-                           attestKey_cb _hidl_cb) override {
-        return dev_->attestKey(keyToAttest, attestParams, _hidl_cb);
-    }
-
-    Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
-                            const hidl_vec<KeyParameter>& upgradeParams,
-                            upgradeKey_cb _hidl_cb) override {
-        return dev_->upgradeKey(keyBlobToUpgrade, upgradeParams, _hidl_cb);
-    }
-
-    Return<ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override {
-        return dev_->deleteKey(keyBlob);
-    }
-
-    Return<ErrorCode> deleteAllKeys() override { return dev_->deleteAllKeys(); }
-
-    Return<ErrorCode> destroyAttestationIds() override { return dev_->destroyAttestationIds(); }
-
-    Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
-                       const hidl_vec<KeyParameter>& inParams, const HardwareAuthToken& authToken,
-                       begin_cb _hidl_cb) override {
-        return dev_->begin(purpose, key, inParams, authToken, _hidl_cb);
-    }
-
-    Return<void> update(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
-                        const hidl_vec<uint8_t>& input, const HardwareAuthToken& authToken,
-                        const VerificationToken& verificationToken, update_cb _hidl_cb) override {
-        return dev_->update(operationHandle, inParams, input, authToken, verificationToken,
-                            _hidl_cb);
-    }
-
-    Return<void> finish(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
-                        const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,
-                        const HardwareAuthToken& authToken,
-                        const VerificationToken& verificationToken, finish_cb _hidl_cb) override {
-        return dev_->finish(operationHandle, inParams, input, signature, authToken,
-                            verificationToken, _hidl_cb);
-    }
-
-    Return<ErrorCode> abort(uint64_t operationHandle) override {
-        return dev_->abort(operationHandle);
-    }
-
-   private:
-    void getVersionIfNeeded();
-
-    bool haveVersion_;
-    VersionResult version_;
-    sp<IKeymaster4Device> dev_;
-};
-
-}  // namespace support
-}  // namespace V4_0
-}  // namespace keymaster
-}  // namespace hardware
-}  // namespace android
-
-#endif  // HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_4_H_
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 cb29c64..bc7f311 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
@@ -355,95 +355,61 @@
     return accessTagValue(ttag, param);
 }
 
+inline bool operator<(const KeyParameter& a, const KeyParameter& b) {
+    if (a.tag != b.tag) return a.tag < b.tag;
+    int retval;
+    switch (typeFromTag(a.tag)) {
+        case TagType::INVALID:
+        case TagType::BOOL:
+            return false;
+        case TagType::ENUM:
+        case TagType::ENUM_REP:
+        case TagType::UINT:
+        case TagType::UINT_REP:
+            return a.f.integer < b.f.integer;
+        case TagType::ULONG:
+        case TagType::ULONG_REP:
+            return a.f.longInteger < b.f.longInteger;
+        case TagType::DATE:
+            return a.f.dateTime < b.f.dateTime;
+        case TagType::BIGNUM:
+        case TagType::BYTES:
+            // Handle the empty cases.
+            if (a.blob.size() == 0) return b.blob.size() != 0;
+            if (b.blob.size() == 0) return false;
+
+            retval = memcmp(&a.blob[0], &b.blob[0], std::min(a.blob.size(), b.blob.size()));
+            // if one is the prefix of the other the longer wins
+            if (retval == 0) return a.blob.size() < b.blob.size();
+            // Otherwise a is less if a is less.
+            else
+                return retval < 0;
+    }
+    return false;
+}
+
 inline bool operator==(const KeyParameter& a, const KeyParameter& b) {
-    if (a.tag != b.tag) {
-        return false;
-    }
+    if (a.tag != b.tag) return false;
 
-    switch (a.tag) {
-        /* Boolean tags */
-        case Tag::INVALID:
-        case Tag::CALLER_NONCE:
-        case Tag::INCLUDE_UNIQUE_ID:
-        case Tag::BOOTLOADER_ONLY:
-        case Tag::NO_AUTH_REQUIRED:
-        case Tag::ALLOW_WHILE_ON_BODY:
-        case Tag::UNLOCKED_DEVICE_REQUIRED:
-        case Tag::ROLLBACK_RESISTANCE:
-        case Tag::RESET_SINCE_ID_ROTATION:
-        case Tag::TRUSTED_CONFIRMATION_REQUIRED:
-        case Tag::TRUSTED_USER_PRESENCE_REQUIRED:
+    switch (typeFromTag(a.tag)) {
+        case TagType::INVALID:
+        case TagType::BOOL:
             return true;
-
-        /* Integer tags */
-        case Tag::KEY_SIZE:
-        case Tag::MIN_MAC_LENGTH:
-        case Tag::MIN_SECONDS_BETWEEN_OPS:
-        case Tag::MAX_USES_PER_BOOT:
-        case Tag::OS_VERSION:
-        case Tag::OS_PATCHLEVEL:
-        case Tag::MAC_LENGTH:
-        case Tag::USER_ID:
-        case Tag::AUTH_TIMEOUT:
-        case Tag::VENDOR_PATCHLEVEL:
-        case Tag::BOOT_PATCHLEVEL:
+        case TagType::ENUM:
+        case TagType::ENUM_REP:
+        case TagType::UINT:
+        case TagType::UINT_REP:
             return a.f.integer == b.f.integer;
-
-        /* Long integer tags */
-        case Tag::RSA_PUBLIC_EXPONENT:
-        case Tag::USER_SECURE_ID:
+        case TagType::ULONG:
+        case TagType::ULONG_REP:
             return a.f.longInteger == b.f.longInteger;
-
-        /* Date-time tags */
-        case Tag::ACTIVE_DATETIME:
-        case Tag::ORIGINATION_EXPIRE_DATETIME:
-        case Tag::USAGE_EXPIRE_DATETIME:
-        case Tag::CREATION_DATETIME:
+        case TagType::DATE:
             return a.f.dateTime == b.f.dateTime;
-
-        /* Bytes tags */
-        case Tag::APPLICATION_ID:
-        case Tag::APPLICATION_DATA:
-        case Tag::ROOT_OF_TRUST:
-        case Tag::UNIQUE_ID:
-        case Tag::ATTESTATION_CHALLENGE:
-        case Tag::ATTESTATION_APPLICATION_ID:
-        case Tag::ATTESTATION_ID_BRAND:
-        case Tag::ATTESTATION_ID_DEVICE:
-        case Tag::ATTESTATION_ID_PRODUCT:
-        case Tag::ATTESTATION_ID_SERIAL:
-        case Tag::ATTESTATION_ID_IMEI:
-        case Tag::ATTESTATION_ID_MEID:
-        case Tag::ATTESTATION_ID_MANUFACTURER:
-        case Tag::ATTESTATION_ID_MODEL:
-        case Tag::ASSOCIATED_DATA:
-        case Tag::CONFIRMATION_TOKEN:
-        case Tag::NONCE:
-            return a.blob == b.blob;
-
-        /* Enum tags */
-        case Tag::PURPOSE:
-            return a.f.purpose == b.f.purpose;
-        case Tag::ALGORITHM:
-            return a.f.algorithm == b.f.algorithm;
-        case Tag::BLOCK_MODE:
-            return a.f.blockMode == b.f.blockMode;
-        case Tag::DIGEST:
-            return a.f.digest == b.f.digest;
-        case Tag::PADDING:
-            return a.f.paddingMode == b.f.paddingMode;
-        case Tag::EC_CURVE:
-            return a.f.ecCurve == b.f.ecCurve;
-        case Tag::BLOB_USAGE_REQUIREMENTS:
-            return a.f.keyBlobUsageRequirements == b.f.keyBlobUsageRequirements;
-        case Tag::USER_AUTH_TYPE:
-            return a.f.integer == b.f.integer;
-        case Tag::ORIGIN:
-            return a.f.origin == b.f.origin;
-        case Tag::HARDWARE_TYPE:
-            return a.f.hardwareType == b.f.hardwareType;
+        case TagType::BIGNUM:
+        case TagType::BYTES:
+            if (a.blob.size() != b.blob.size()) return false;
+            return a.blob.size() == 0 || memcmp(&a.blob[0], &b.blob[0], a.blob.size()) == 0;
     }
-
     return false;
 }
 
diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h
index 5e5ae8d..61645f8 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h
@@ -52,6 +52,9 @@
 HardwareAuthToken hidlVec2AuthToken(const hidl_vec<uint8_t>& buffer);
 hidl_vec<uint8_t> authToken2HidlVec(const HardwareAuthToken& token);
 
+uint32_t getOsVersion();
+uint32_t getOsPatchlevel();
+
 }  // namespace support
 }  // namespace V4_0
 }  // namespace keymaster
diff --git a/keymaster/4.0/support/include/keymasterV4_0/openssl_utils.h b/keymaster/4.0/support/include/keymasterV4_0/openssl_utils.h
index cc71dd1..b3869f4 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/openssl_utils.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/openssl_utils.h
@@ -18,6 +18,8 @@
 #define HARDWARE_INTERFACES_KEYMASTER_4_0_SUPPORT_OPENSSL_UTILS_H_
 
 #include <android/hardware/keymaster/4.0/types.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
 
 template <typename T, void (*F)(T*)>
 struct UniquePtrDeleter {
diff --git a/keymaster/4.0/support/keymaster_utils.cpp b/keymaster/4.0/support/keymaster_utils.cpp
index e35fdd3..850a776 100644
--- a/keymaster/4.0/support/keymaster_utils.cpp
+++ b/keymaster/4.0/support/keymaster_utils.cpp
@@ -14,11 +14,13 @@
  * limitations under the License.
  */
 
+#include <regex.h>
+
+#include <android-base/properties.h>
 #include <hardware/hw_auth_token.h>
 #include <keymasterV4_0/keymaster_utils.h>
 
-namespace android {
-namespace hardware {
+namespace android::hardware {
 
 inline static bool operator<(const hidl_vec<uint8_t>& a, const hidl_vec<uint8_t>& b) {
     auto result = memcmp(a.data(), b.data(), std::min(a.size(), b.size()));
@@ -32,8 +34,7 @@
     return memcmp(a.data(), b.data(), SIZE) == -1;
 }
 
-namespace keymaster {
-namespace V4_0 {
+namespace keymaster::V4_0 {
 
 bool operator<(const HmacSharingParameters& a, const HmacSharingParameters& b) {
     return std::tie(a.seed, a.nonce) < std::tie(b.seed, b.nonce);
@@ -58,9 +59,9 @@
 
 hidl_vec<uint8_t> authToken2HidlVec(const HardwareAuthToken& token) {
     static_assert(1 /* version size */ + sizeof(token.challenge) + sizeof(token.userId) +
-                          sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
-                          sizeof(token.timestamp) + kHmacSize ==
-                      sizeof(hw_auth_token_t),
+                                  sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
+                                  sizeof(token.timestamp) + kHmacSize ==
+                          sizeof(hw_auth_token_t),
                   "HardwareAuthToken content size does not match hw_auth_token_t size");
 
     hidl_vec<uint8_t> result;
@@ -86,9 +87,9 @@
 HardwareAuthToken hidlVec2AuthToken(const hidl_vec<uint8_t>& buffer) {
     HardwareAuthToken token;
     static_assert(1 /* version size */ + sizeof(token.challenge) + sizeof(token.userId) +
-                          sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
-                          sizeof(token.timestamp) + kHmacSize ==
-                      sizeof(hw_auth_token_t),
+                                  sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
+                                  sizeof(token.timestamp) + kHmacSize ==
+                          sizeof(hw_auth_token_t),
                   "HardwareAuthToken content size does not match hw_auth_token_t size");
 
     if (buffer.size() != sizeof(hw_auth_token_t)) return {};
@@ -100,7 +101,7 @@
     pos = copy_bytes_from_iterator(&token.authenticatorId, pos);
     pos = copy_bytes_from_iterator(&token.authenticatorType, pos);
     token.authenticatorType = static_cast<HardwareAuthenticatorType>(
-        ntohl(static_cast<uint32_t>(token.authenticatorType)));
+            ntohl(static_cast<uint32_t>(token.authenticatorType)));
     pos = copy_bytes_from_iterator(&token.timestamp, pos);
     token.timestamp = ntohq(token.timestamp);
     token.mac.resize(kHmacSize);
@@ -109,8 +110,93 @@
     return token;
 }
 
+namespace {
+
+constexpr char kPlatformVersionProp[] = "ro.build.version.release";
+constexpr char kPlatformVersionRegex[] = "^([0-9]{1,2})(\\.([0-9]{1,2}))?(\\.([0-9]{1,2}))?";
+constexpr size_t kMajorVersionMatch = 1;
+constexpr size_t kMinorVersionMatch = 3;
+constexpr size_t kSubminorVersionMatch = 5;
+constexpr size_t kPlatformVersionMatchCount = kSubminorVersionMatch + 1;
+
+constexpr char kPlatformPatchlevelProp[] = "ro.build.version.security_patch";
+constexpr char kPlatformPatchlevelRegex[] = "^([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;
+
+uint32_t match_to_uint32(const char* expression, const regmatch_t& match) {
+    if (match.rm_so == -1) return 0;
+
+    size_t len = match.rm_eo - match.rm_so;
+    std::string s(expression + match.rm_so, len);
+    return std::stoul(s);
+}
+
+std::string wait_and_get_property(const char* prop) {
+    std::string prop_value;
+    while (!android::base::WaitForPropertyCreation(prop))
+        ;
+    prop_value = android::base::GetProperty(prop, "" /* default */);
+    return prop_value;
+}
+
+}  // anonymous namespace
+
+uint32_t getOsVersion(const char* version_str) {
+    regex_t regex;
+    if (regcomp(&regex, kPlatformVersionRegex, REG_EXTENDED)) {
+        return 0;
+    }
+
+    regmatch_t matches[kPlatformVersionMatchCount];
+    int not_match =
+            regexec(&regex, version_str, kPlatformVersionMatchCount, matches, 0 /* flags */);
+    regfree(&regex);
+    if (not_match) {
+        return 0;
+    }
+
+    uint32_t major = match_to_uint32(version_str, matches[kMajorVersionMatch]);
+    uint32_t minor = match_to_uint32(version_str, matches[kMinorVersionMatch]);
+    uint32_t subminor = match_to_uint32(version_str, matches[kSubminorVersionMatch]);
+
+    return (major * 100 + minor) * 100 + subminor;
+}
+
+uint32_t getOsVersion() {
+    std::string version = wait_and_get_property(kPlatformVersionProp);
+    return getOsVersion(version.c_str());
+}
+
+uint32_t getOsPatchlevel(const char* patchlevel_str) {
+    regex_t regex;
+    if (regcomp(&regex, kPlatformPatchlevelRegex, REG_EXTENDED) != 0) {
+        return 0;
+    }
+
+    regmatch_t matches[kPlatformPatchlevelMatchCount];
+    int not_match =
+            regexec(&regex, patchlevel_str, kPlatformPatchlevelMatchCount, matches, 0 /* flags */);
+    regfree(&regex);
+    if (not_match) {
+        return 0;
+    }
+
+    uint32_t year = match_to_uint32(patchlevel_str, matches[kYearMatch]);
+    uint32_t month = match_to_uint32(patchlevel_str, matches[kMonthMatch]);
+
+    if (month < 1 || month > 12) {
+        return 0;
+    }
+    return year * 100 + month;
+}
+
+uint32_t getOsPatchlevel() {
+    std::string patchlevel = wait_and_get_property(kPlatformPatchlevelProp);
+    return getOsPatchlevel(patchlevel.c_str());
+}
+
 }  // namespace support
-}  // namespace V4_0
-}  // namespace keymaster
-}  // namespace hardware
-}  // namespace android
+}  // namespace keymaster::V4_0
+}  // namespace android::hardware
diff --git a/keymaster/4.0/vts/functional/Android.bp b/keymaster/4.0/vts/functional/Android.bp
index 5649f20..db50080 100644
--- a/keymaster/4.0/vts/functional/Android.bp
+++ b/keymaster/4.0/vts/functional/Android.bp
@@ -19,7 +19,6 @@
     defaults: ["VtsHalTargetTestDefaults"],
     srcs: [
         "HmacKeySharingTest.cpp",
-        "KeymasterHidlTest.cpp",
         "VerificationTokenTest.cpp",
         "keymaster_hidl_hal_test.cpp",
     ],
@@ -27,7 +26,25 @@
         "android.hardware.keymaster@4.0",
         "libcrypto_static",
         "libkeymaster4support",
-        "libsoftkeymasterdevice",
+        "libkeymaster4vtstest",
     ],
-    test_suites: ["general-tests", "vts-core"],
+    test_suites: [
+        "general-tests",
+        "vts-core",
+    ],
+}
+
+cc_test_library {
+    name: "libkeymaster4vtstest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "KeymasterHidlTest.cpp",
+    ],
+    export_include_dirs: [
+        ".",
+    ],
+    static_libs: [
+        "android.hardware.keymaster@4.0",
+        "libkeymaster4support",
+    ],
 }
diff --git a/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp b/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp
index c228ef7..f57a668 100644
--- a/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp
+++ b/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp
@@ -28,6 +28,16 @@
  */
 class HmacKeySharingTest : public KeymasterHidlTest {
    protected:
+     const std::vector<sp<IKeymasterDevice>>& allKeymasters() {
+         if (all_keymasters_.empty()) {
+             auto names = android::hardware::getAllHalInstanceNames(IKeymasterDevice::descriptor);
+             for (const auto& name : names) {
+                 all_keymasters_.push_back(IKeymasterDevice::getService(name));
+             }
+         }
+         return all_keymasters_;
+     }
+
     struct GetParamsResult {
         ErrorCode error;
         HmacSharingParameters params;
@@ -99,8 +109,13 @@
             EXPECT_EQ(expected, response.sharing_check) << "Sharing check values should match.";
         }
     }
+
+  private:
+    static std::vector<sp<IKeymasterDevice>> all_keymasters_;
 };
 
+std::vector<sp<IKeymasterDevice>> HmacKeySharingTest::all_keymasters_;
+
 TEST_P(HmacKeySharingTest, GetParameters) {
     auto result1 = getHmacSharingParameters(keymaster());
     EXPECT_EQ(ErrorCode::OK, result1.error);
@@ -115,26 +130,26 @@
 }
 
 TEST_P(HmacKeySharingTest, ComputeSharedHmac) {
-    auto params = getHmacSharingParameters(all_keymasters());
-    ASSERT_EQ(all_keymasters().size(), params.size())
-        << "One or more keymasters failed to provide parameters.";
+    auto params = getHmacSharingParameters(allKeymasters());
+    ASSERT_EQ(allKeymasters().size(), params.size())
+            << "One or more keymasters failed to provide parameters.";
 
     auto nonces = copyNonces(params);
-    EXPECT_EQ(all_keymasters().size(), nonces.size());
+    EXPECT_EQ(allKeymasters().size(), nonces.size());
     std::sort(nonces.begin(), nonces.end());
     std::unique(nonces.begin(), nonces.end());
-    EXPECT_EQ(all_keymasters().size(), nonces.size());
+    EXPECT_EQ(allKeymasters().size(), nonces.size());
 
-    auto responses = computeSharedHmac(all_keymasters(), params);
+    auto responses = computeSharedHmac(allKeymasters(), params);
     ASSERT_GT(responses.size(), 0U);
     verifyResponses(responses[0].sharing_check, responses);
 
     // Do it a second time.  Should get the same answers.
-    params = getHmacSharingParameters(all_keymasters());
-    ASSERT_EQ(all_keymasters().size(), params.size())
-        << "One or more keymasters failed to provide parameters.";
+    params = getHmacSharingParameters(allKeymasters());
+    ASSERT_EQ(allKeymasters().size(), params.size())
+            << "One or more keymasters failed to provide parameters.";
 
-    responses = computeSharedHmac(all_keymasters(), params);
+    responses = computeSharedHmac(allKeymasters(), params);
     ASSERT_GT(responses.size(), 0U);
     ASSERT_EQ(32U, responses[0].sharing_check.size());
     verifyResponses(responses[0].sharing_check, responses);
@@ -160,15 +175,16 @@
     // sync with respect to the HMAC key.  Granted that VTS tests aren't run on in-use production
     // devices, this still has the potential to cause confusion.  To mitigate that, we always
     // (barring crashes :-/) re-run the unmodified agreement process on our way out.
-    auto fixup_hmac = finally(
-        [&]() { computeSharedHmac(all_keymasters(), getHmacSharingParameters(all_keymasters())); });
+    auto fixup_hmac = finally([&]() {
+        computeSharedHmac(allKeymasters(), getHmacSharingParameters(allKeymasters()));
+    });
 
-    auto params = getHmacSharingParameters(all_keymasters());
-    ASSERT_EQ(all_keymasters().size(), params.size())
-        << "One or more keymasters failed to provide parameters.";
+    auto params = getHmacSharingParameters(allKeymasters());
+    ASSERT_EQ(allKeymasters().size(), params.size())
+            << "One or more keymasters failed to provide parameters.";
 
     // All should be well in the normal case
-    auto responses = computeSharedHmac(all_keymasters(), params);
+    auto responses = computeSharedHmac(allKeymasters(), params);
 
     ASSERT_GT(responses.size(), 0U);
     HidlBuf correct_response = responses[0].sharing_check;
@@ -181,7 +197,7 @@
     uint8_t bit_to_tweak = rand() % 8;
     params[param_to_tweak].nonce[byte_to_tweak] ^= (1 << bit_to_tweak);
 
-    responses = computeSharedHmac(all_keymasters(), params);
+    responses = computeSharedHmac(allKeymasters(), params);
     for (size_t i = 0; i < responses.size(); ++i) {
         if (i == param_to_tweak) {
             EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
@@ -199,15 +215,16 @@
     // sync with respect to the HMAC key.  Granted that VTS tests aren't run on in-use production
     // devices, this still has the potential to cause confusion.  To mitigate that, we always
     // (barring crashes :-/) re-run the unmodified agreement process on our way out.
-    auto fixup_hmac = finally(
-        [&]() { computeSharedHmac(all_keymasters(), getHmacSharingParameters(all_keymasters())); });
+    auto fixup_hmac = finally([&]() {
+        computeSharedHmac(allKeymasters(), getHmacSharingParameters(allKeymasters()));
+    });
 
-    auto params = getHmacSharingParameters(all_keymasters());
-    ASSERT_EQ(all_keymasters().size(), params.size())
-        << "One or more keymasters failed to provide parameters.";
+    auto params = getHmacSharingParameters(allKeymasters());
+    ASSERT_EQ(allKeymasters().size(), params.size())
+            << "One or more keymasters failed to provide parameters.";
 
     // All should be well in the normal case
-    auto responses = computeSharedHmac(all_keymasters(), params);
+    auto responses = computeSharedHmac(allKeymasters(), params);
 
     ASSERT_GT(responses.size(), 0U);
     HidlBuf correct_response = responses[0].sharing_check;
@@ -223,7 +240,7 @@
     }
     to_tweak[0]++;
 
-    responses = computeSharedHmac(all_keymasters(), params);
+    responses = computeSharedHmac(allKeymasters(), params);
     for (size_t i = 0; i < responses.size(); ++i) {
         if (i == param_to_tweak) {
             EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
@@ -236,10 +253,7 @@
     }
 }
 
-INSTANTIATE_TEST_SUITE_P(
-        PerInstance, HmacKeySharingTest,
-        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IKeymasterDevice::descriptor)),
-        android::hardware::PrintInstanceNameToString);
+INSTANTIATE_KEYMASTER_HIDL_TEST(HmacKeySharingTest);
 
 }  // namespace test
 }  // namespace V4_0
diff --git a/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp b/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
index 7241984..d0ad433 100644
--- a/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
+++ b/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
@@ -23,6 +23,7 @@
 #include <android/hidl/manager/1.0/IServiceManager.h>
 
 #include <keymasterV4_0/key_param_output.h>
+#include <keymasterV4_0/keymaster_utils.h>
 
 namespace android {
 namespace hardware {
@@ -41,39 +42,26 @@
 
 namespace test {
 
-void KeymasterHidlTest::InitializeKeymaster() {
-    service_name_ = GetParam();
-    keymaster_ = IKeymasterDevice::getService(service_name_);
-    ASSERT_NE(keymaster_, nullptr);
+using namespace std::literals::chrono_literals;
 
+void KeymasterHidlTest::InitializeKeymaster(sp<IKeymasterDevice> keymaster) {
+    ASSERT_NE(keymaster, nullptr);
+    keymaster_ = keymaster;
     ASSERT_TRUE(keymaster_
-                    ->getHardwareInfo([&](SecurityLevel securityLevel, const hidl_string& name,
-                                          const hidl_string& author) {
-                        securityLevel_ = securityLevel;
-                        name_ = name;
-                        author_ = author;
-                    })
-                    .isOk());
+                        ->getHardwareInfo([&](SecurityLevel securityLevel, const hidl_string& name,
+                                              const hidl_string& author) {
+                            securityLevel_ = securityLevel;
+                            name_ = name;
+                            author_ = author;
+                        })
+                        .isOk());
+
+    os_version_ = support::getOsVersion();
+    os_patch_level_ = support::getOsPatchlevel();
 }
 
 void KeymasterHidlTest::SetUp() {
-    InitializeKeymaster();
-
-    os_version_ = ::keymaster::GetOsVersion();
-    os_patch_level_ = ::keymaster::GetOsPatchlevel();
-
-    auto service_manager = android::hidl::manager::V1_0::IServiceManager::getService();
-    ASSERT_NE(nullptr, service_manager.get());
-    all_keymasters_.push_back(keymaster_);
-    service_manager->listByInterface(
-        IKeymasterDevice::descriptor, [&](const hidl_vec<hidl_string>& names) {
-            for (auto& name : names) {
-                if (name == service_name_) continue;
-                auto keymaster = IKeymasterDevice::getService(name);
-                ASSERT_NE(keymaster, nullptr);
-                all_keymasters_.push_back(keymaster);
-            }
-        });
+    InitializeKeymaster(IKeymasterDevice::getService(GetParam()));
 }
 
 ErrorCode KeymasterHidlTest::GenerateKey(const AuthorizationSet& key_desc, HidlBuf* key_blob,
@@ -138,7 +126,7 @@
                                               string masking_key,
                                               const AuthorizationSet& unwrapping_params) {
     ErrorCode error;
-    ImportKey(wrapping_key_desc, KeyFormat::PKCS8, wrapping_key);
+    EXPECT_EQ(ErrorCode::OK, ImportKey(wrapping_key_desc, KeyFormat::PKCS8, wrapping_key));
     EXPECT_TRUE(keymaster_
                     ->importWrappedKey(HidlBuf(wrapped_key), key_blob_, HidlBuf(masking_key),
                                        unwrapping_params.hidl_data(), 0 /* passwordSid */,
@@ -207,7 +195,9 @@
     HidlBuf empty_buf = {};
     EXPECT_EQ(ErrorCode::OK,
               GetCharacteristics(key_blob, client_id, app_data, key_characteristics));
-    EXPECT_GT(key_characteristics->hardwareEnforced.size(), 0);
+    if (SecLevel() != SecurityLevel::SOFTWARE) {
+        EXPECT_GT(key_characteristics->hardwareEnforced.size(), 0);
+    }
     EXPECT_GT(key_characteristics->softwareEnforced.size(), 0);
 
     EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
@@ -647,23 +637,25 @@
     switch (algorithm) {
         case Algorithm::RSA:
             switch (SecLevel()) {
+                case SecurityLevel::SOFTWARE:
                 case SecurityLevel::TRUSTED_ENVIRONMENT:
                     return {2048, 3072, 4096};
                 case SecurityLevel::STRONGBOX:
                     return {2048};
                 default:
-                    CHECK(false) << "Invalid security level " << uint32_t(SecLevel());
+                    ADD_FAILURE() << "Invalid security level " << uint32_t(SecLevel());
                     break;
             }
             break;
         case Algorithm::EC:
             switch (SecLevel()) {
+                case SecurityLevel::SOFTWARE:
                 case SecurityLevel::TRUSTED_ENVIRONMENT:
                     return {224, 256, 384, 521};
                 case SecurityLevel::STRONGBOX:
                     return {256};
                 default:
-                    CHECK(false) << "Invalid security level " << uint32_t(SecLevel());
+                    ADD_FAILURE() << "Invalid security level " << uint32_t(SecLevel());
                     break;
             }
             break;
@@ -678,25 +670,27 @@
             return retval;
         }
         default:
-            CHECK(false) << "Invalid Algorithm: " << algorithm;
+            ADD_FAILURE() << "Invalid Algorithm: " << algorithm;
             return {};
     }
-    CHECK(false) << "Should be impossible to get here";
+    ADD_FAILURE() << "Should be impossible to get here";
     return {};
 }
+
 std::vector<uint32_t> KeymasterHidlTest::InvalidKeySizes(Algorithm algorithm) {
-    if (SecLevel() == SecurityLevel::TRUSTED_ENVIRONMENT) return {};
-    CHECK(SecLevel() == SecurityLevel::STRONGBOX);
-    switch (algorithm) {
-        case Algorithm::RSA:
-            return {3072, 4096};
-        case Algorithm::EC:
-            return {224, 384, 521};
-        case Algorithm::AES:
-            return {192};
-        default:
-            return {};
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        switch (algorithm) {
+            case Algorithm::RSA:
+                return {3072, 4096};
+            case Algorithm::EC:
+                return {224, 384, 521};
+            case Algorithm::AES:
+                return {192};
+            default:
+                return {};
+        }
     }
+    return {};
 }
 
 std::vector<EcCurve> KeymasterHidlTest::ValidCurves() {
@@ -715,6 +709,7 @@
 
 std::vector<Digest> KeymasterHidlTest::ValidDigests(bool withNone, bool withMD5) {
     switch (SecLevel()) {
+        case SecurityLevel::SOFTWARE:
         case SecurityLevel::TRUSTED_ENVIRONMENT:
             if (withNone) {
                 if (withMD5)
@@ -740,10 +735,10 @@
                 return {Digest::SHA_2_256};
             break;
         default:
-            CHECK(false) << "Invalid security level " << uint32_t(SecLevel());
+            ADD_FAILURE() << "Invalid security level " << uint32_t(SecLevel());
             break;
     }
-    CHECK(false) << "Should be impossible to get here";
+    ADD_FAILURE() << "Should be impossible to get here";
     return {};
 }
 
diff --git a/keymaster/4.0/vts/functional/KeymasterHidlTest.h b/keymaster/4.0/vts/functional/KeymasterHidlTest.h
index 4bd8b26..f495516 100644
--- a/keymaster/4.0/vts/functional/KeymasterHidlTest.h
+++ b/keymaster/4.0/vts/functional/KeymasterHidlTest.h
@@ -21,7 +21,6 @@
 #include <gtest/gtest.h>
 #include <hidl/GtestPrinter.h>
 #include <hidl/ServiceManagement.h>
-#include <keymaster/keymaster_configuration.h>
 
 #include <keymasterV4_0/authorization_set.h>
 
@@ -35,16 +34,18 @@
 namespace test {
 
 using ::android::sp;
-using ::std::string;
 using hidl::base::V1_0::DebugInfo;
+using ::std::string;
 
 class HidlBuf : public hidl_vec<uint8_t> {
-    typedef hidl_vec<uint8_t> super;
+    using super = hidl_vec<uint8_t>;
 
-   public:
+  public:
     HidlBuf() {}
     HidlBuf(const super& other) : super(other) {}
-    HidlBuf(super&& other) : super(std::move(other)) {}
+    HidlBuf(super&& other) : super(std::move(other)) { other = {}; }
+    HidlBuf(const HidlBuf& other) : super(other) {}
+    HidlBuf(HidlBuf&& other) : super(std::move(other)) { other = HidlBuf(); }
     explicit HidlBuf(const std::string& other) : HidlBuf() { *this = other; }
 
     HidlBuf& operator=(const super& other) {
@@ -54,6 +55,18 @@
 
     HidlBuf& operator=(super&& other) {
         super::operator=(std::move(other));
+        other = {};
+        return *this;
+    }
+
+    HidlBuf& operator=(const HidlBuf& other) {
+        super::operator=(other);
+        return *this;
+    }
+
+    HidlBuf& operator=(HidlBuf&& other) {
+        super::operator=(std::move(other));
+        other.super::operator=({});
         return *this;
     }
 
@@ -70,20 +83,16 @@
 
 class KeymasterHidlTest : public ::testing::TestWithParam<std::string> {
   public:
-    void SetUp();
+    void SetUp() override;
     void TearDown() override {
         if (key_blob_.size()) {
             CheckedDeleteKey();
         }
         AbortIfNeeded();
-        keymaster_.clear();
-        all_keymasters_.clear();
     }
 
-    void InitializeKeymaster();
-
+    void InitializeKeymaster(sp<IKeymasterDevice> keymaster);
     IKeymasterDevice& keymaster() { return *keymaster_; }
-    const std::vector<sp<IKeymasterDevice>>& all_keymasters() { return all_keymasters_; }
     uint32_t os_version() { return os_version_; }
     uint32_t os_patch_level() { return os_patch_level_; }
 
@@ -209,18 +218,26 @@
     KeyCharacteristics key_characteristics_;
     OperationHandle op_handle_ = kOpHandleSentinel;
 
-   private:
-     sp<IKeymasterDevice> keymaster_;
-     std::vector<sp<IKeymasterDevice>> all_keymasters_;
-     uint32_t os_version_;
-     uint32_t os_patch_level_;
+    static std::vector<std::string> build_params() {
+        auto params = android::hardware::getAllHalInstanceNames(IKeymasterDevice::descriptor);
+        return params;
+    }
 
-     SecurityLevel securityLevel_;
-     hidl_string name_;
-     hidl_string author_;
-     string service_name_;
+  private:
+    sp<IKeymasterDevice> keymaster_;
+    uint32_t os_version_;
+    uint32_t os_patch_level_;
+
+    SecurityLevel securityLevel_;
+    hidl_string name_;
+    hidl_string author_;
 };
 
+#define INSTANTIATE_KEYMASTER_HIDL_TEST(name)                                      \
+    INSTANTIATE_TEST_SUITE_P(PerInstance, name,                                    \
+                             testing::ValuesIn(KeymasterHidlTest::build_params()), \
+                             android::hardware::PrintInstanceNameToString)
+
 }  // namespace test
 }  // namespace V4_0
 }  // namespace keymaster
diff --git a/keymaster/4.0/vts/functional/VerificationTokenTest.cpp b/keymaster/4.0/vts/functional/VerificationTokenTest.cpp
index 693f4ae..bab1439 100644
--- a/keymaster/4.0/vts/functional/VerificationTokenTest.cpp
+++ b/keymaster/4.0/vts/functional/VerificationTokenTest.cpp
@@ -185,10 +185,7 @@
               memcmp(result1.token.mac.data(), result2.token.mac.data(), result1.token.mac.size()));
 }
 
-INSTANTIATE_TEST_SUITE_P(
-        PerInstance, VerificationTokenTest,
-        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IKeymasterDevice::descriptor)),
-        android::hardware::PrintInstanceNameToString);
+INSTANTIATE_KEYMASTER_HIDL_TEST(VerificationTokenTest);
 
 }  // namespace test
 }  // namespace V4_0
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 66132ad..6cbe4da 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -352,11 +352,11 @@
     EXPECT_EQ(ErrorCode::OK, error);
     if (error != ErrorCode::OK) return false;
 
-    EXPECT_TRUE(att_attestation_version == 3);
+    EXPECT_GE(att_attestation_version, 3U);
 
     expected_sw_enforced.push_back(TAG_ATTESTATION_APPLICATION_ID, HidlBuf(app_id));
 
-    EXPECT_EQ(att_keymaster_version, 4U);
+    EXPECT_GE(att_keymaster_version, 4U);
     EXPECT_EQ(security_level, att_keymaster_security_level);
     EXPECT_EQ(security_level, att_attestation_security_level);
 
@@ -397,10 +397,16 @@
     // 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.
-    EXPECT_TRUE(expected_hw_enforced.Contains(TAG_NO_AUTH_REQUIRED));
+    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));
 
@@ -461,10 +467,10 @@
                             verified_boot_key.size()));
     } else if (!strcmp(property_value, "red")) {
         EXPECT_EQ(verified_boot_state, KM_VERIFIED_BOOT_FAILED);
-        EXPECT_EQ(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
-                            verified_boot_key.size()));
     } else {
-        EXPECT_TRUE(false);
+        EXPECT_EQ(verified_boot_state, KM_VERIFIED_BOOT_UNVERIFIED);
+        EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
+                            verified_boot_key.size()));
     }
 
     att_sw_enforced.Sort();
@@ -840,6 +846,8 @@
                               .Authorization(TAG_MIN_MAC_LENGTH, 128)));
 }
 
+INSTANTIATE_KEYMASTER_HIDL_TEST(NewKeyGenerationTest);
+
 typedef KeymasterHidlTest SigningOperationsTest;
 
 /*
@@ -1509,6 +1517,8 @@
     }
 }
 
+INSTANTIATE_KEYMASTER_HIDL_TEST(SigningOperationsTest);
+
 typedef KeymasterHidlTest VerificationOperationsTest;
 
 /*
@@ -1749,6 +1759,8 @@
     CheckedDeleteKey(&verification_key);
 }
 
+INSTANTIATE_KEYMASTER_HIDL_TEST(VerificationOperationsTest);
+
 typedef KeymasterHidlTest ExportKeyTest;
 
 /*
@@ -1828,6 +1840,8 @@
     EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_FORMAT, ExportKey(KeyFormat::RAW, &export_data));
 }
 
+INSTANTIATE_KEYMASTER_HIDL_TEST(ExportKeyTest);
+
 class ImportKeyTest : public KeymasterHidlTest {
    public:
     template <TagType tag_type, Tag tag, typename ValueT>
@@ -2093,6 +2107,8 @@
     VerifyMessage(message, signature, AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
 }
 
+INSTANTIATE_KEYMASTER_HIDL_TEST(ImportKeyTest);
+
 auto wrapped_key = hex2str(
     "3082017902010004820100934bf94e2aa28a3f83c9f79297250262fbe3276b5a1c91159bbfa3ef8957aac84b59b30b"
     "455a79c2973480823d8b3863c3deef4a8e243590268d80e18751a0e130f67ce6a1ace9f79b95e097474febc981195b"
@@ -2214,6 +2230,8 @@
                       .Padding(PaddingMode::RSA_OAEP)));
 }
 
+INSTANTIATE_KEYMASTER_HIDL_TEST(ImportWrappedKeyTest);
+
 typedef KeymasterHidlTest EncryptionOperationsTest;
 
 /*
@@ -4111,6 +4129,8 @@
     EXPECT_EQ(message, plaintext);
 }
 
+INSTANTIATE_KEYMASTER_HIDL_TEST(EncryptionOperationsTest);
+
 typedef KeymasterHidlTest MaxOperationsTest;
 
 /*
@@ -4166,6 +4186,8 @@
     EXPECT_EQ(ErrorCode::KEY_MAX_OPS_EXCEEDED, Begin(KeyPurpose::SIGN, params));
 }
 
+INSTANTIATE_KEYMASTER_HIDL_TEST(MaxOperationsTest);
+
 typedef KeymasterHidlTest AddEntropyTest;
 
 /*
@@ -4196,6 +4218,8 @@
     EXPECT_EQ(ErrorCode::OK, keymaster().addRngEntropy(HidlBuf(string(2 * 1024, 'a'))));
 }
 
+INSTANTIATE_KEYMASTER_HIDL_TEST(AddEntropyTest);
+
 typedef KeymasterHidlTest AttestationTest;
 
 /*
@@ -4373,6 +4397,8 @@
                         &cert_chain));
 }
 
+INSTANTIATE_KEYMASTER_HIDL_TEST(AttestationTest);
+
 typedef KeymasterHidlTest KeyDeletionTest;
 
 /**
@@ -4478,6 +4504,8 @@
     }
 }
 
+INSTANTIATE_KEYMASTER_HIDL_TEST(KeyDeletionTest);
+
 using UpgradeKeyTest = KeymasterHidlTest;
 
 /*
@@ -4497,6 +4525,8 @@
     EXPECT_EQ(result, std::make_pair(ErrorCode::OK, HidlBuf()));
 }
 
+INSTANTIATE_KEYMASTER_HIDL_TEST(UpgradeKeyTest);
+
 using ClearOperationsTest = KeymasterHidlTest;
 
 /*
@@ -4533,51 +4563,14 @@
     AbortIfNeeded();
 }
 
-/*
- * ClearSlotsTest.ServiceDeath
- *
- * Verifies that the service is restarted after death and the ongoing
- * operations are cleared.
- */
-TEST_P(ClearOperationsTest, ServiceDeath) {
-    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                             .Authorization(TAG_NO_AUTH_REQUIRED)
-                                             .RsaEncryptionKey(2048, 65537)
-                                             .Padding(PaddingMode::NONE)));
-
-    auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
-    int max_operations = SecLevel() == SecurityLevel::STRONGBOX ? 4 : 16;
-    OperationHandle op_handles[max_operations];
-    AuthorizationSet out_params;
-    for(int i=0; i<max_operations; i++) {
-        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &(op_handles[i])));
-    }
-    EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS,
-         Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &op_handle_));
-
-    DebugInfo debug_info;
-    GetDebugInfo(&debug_info);
-    kill(debug_info.pid, SIGKILL);
-    // wait 1 second for keymaster to restart
-    sleep(1);
-    InitializeKeymaster();
-
-    for(int i=0; i<max_operations; i++) {
-        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &(op_handles[i])));
-    }
-    EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS,
-         Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &op_handle_));
-    for(int i=0; i<max_operations; i++) {
-        EXPECT_EQ(ErrorCode::OK, Abort(op_handles[i]));
-    }
-}
+INSTANTIATE_KEYMASTER_HIDL_TEST(ClearOperationsTest);
 
 typedef KeymasterHidlTest TransportLimitTest;
 
 /*
- * TransportLimitTest.LargeFinishInput
+ * TransportLimitTest.FinishInput
  *
- * Verifies that passing large input data to finish either succeeds or fails as expected.
+ * Verifies that passing input data to finish succeeds as expected.
  */
 TEST_P(TransportLimitTest, LargeFinishInput) {
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -4586,7 +4579,7 @@
                                                  .BlockMode(BlockMode::ECB)
                                                  .Padding(PaddingMode::NONE)));
 
-    for (int msg_size = 10 /*1KB*/; msg_size <= 17 /*128KB*/; msg_size++) {
+    for (int msg_size = 8 /* 256 bytes */; msg_size <= 11 /* 2 KiB */; msg_size++) {
         auto cipher_params =
                 AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
 
@@ -4597,71 +4590,22 @@
         string encrypted_message;
         auto rc = Finish(plain_message, &encrypted_message);
 
-        if (rc == ErrorCode::OK) {
-            EXPECT_EQ(plain_message.size(), encrypted_message.size())
-                    << "Encrypt finish returned OK, but did not consume all of the given input";
-        } else {
-            EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, rc)
-                    << "Encrypt finish failed in an unexpected way when given a large input";
-            continue;
-        }
+        EXPECT_EQ(ErrorCode::OK, rc);
+        EXPECT_EQ(plain_message.size(), encrypted_message.size())
+                << "Encrypt finish returned OK, but did not consume all of the given input";
         cipher_params.push_back(out_params);
 
         EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, cipher_params));
 
         string decrypted_message;
         rc = Finish(encrypted_message, &decrypted_message);
-
-        if (rc == ErrorCode::OK) {
-            EXPECT_EQ(plain_message.size(), decrypted_message.size())
-                    << "Decrypt finish returned OK, did not consume all of the given input";
-        } else {
-            EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, rc)
-                    << "Encrypt finish failed in an unexpected way when given a large input";
-        }
+        EXPECT_EQ(ErrorCode::OK, rc);
+        EXPECT_EQ(plain_message.size(), decrypted_message.size())
+                << "Decrypt finish returned OK, did not consume all of the given input";
     }
-
-    CheckedDeleteKey();
 }
 
-static const auto kKeymasterDeviceChoices =
-        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IKeymasterDevice::descriptor));
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, NewKeyGenerationTest, kKeymasterDeviceChoices,
-                         android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, ImportKeyTest, kKeymasterDeviceChoices,
-                         android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, ImportWrappedKeyTest, kKeymasterDeviceChoices,
-                         android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, SigningOperationsTest, kKeymasterDeviceChoices,
-                         android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, VerificationOperationsTest, kKeymasterDeviceChoices,
-                         android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, ExportKeyTest, kKeymasterDeviceChoices,
-                         android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, EncryptionOperationsTest, kKeymasterDeviceChoices,
-                         android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, MaxOperationsTest, kKeymasterDeviceChoices,
-                         android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, AddEntropyTest, kKeymasterDeviceChoices,
-                         android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, AttestationTest, kKeymasterDeviceChoices,
-                         android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, KeyDeletionTest, kKeymasterDeviceChoices,
-                         android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, TransportLimitTest, kKeymasterDeviceChoices,
-                         android::hardware::PrintInstanceNameToString);
+INSTANTIATE_KEYMASTER_HIDL_TEST(TransportLimitTest);
 
 }  // namespace test
 }  // namespace V4_0
diff --git a/keymaster/4.1/IKeymasterDevice.hal b/keymaster/4.1/IKeymasterDevice.hal
index 64d2c9f..1456abe 100644
--- a/keymaster/4.1/IKeymasterDevice.hal
+++ b/keymaster/4.1/IKeymasterDevice.hal
@@ -22,6 +22,8 @@
 import @4.0::KeyParameter;
 import @4.0::KeyPurpose;
 import @4.0::OperationHandle;
+import @4.0::VerificationToken;
+
 import IOperation;
 
 /**
@@ -31,6 +33,11 @@
  * - Device-unique attestaion;
  * - Early boot only keys;
  * - Better cleanup of operations when clients die without completing or aborting them.
+ *
+ * @4.1::IKeymasterDevice::attestKey() must produce attestations with keymasterVersion 41.  An
+ * oversight in the original numbering left no room for minor versions, so starting with 4.1 the
+ * versions will be numbered as major_version * 10 + minor version.  The addition of new attestable
+ * tags changes the attestation format again, slightly, so the attestationVersion must be 4.
  */
 interface IKeymasterDevice extends @4.0::IKeymasterDevice {
     /**
@@ -42,10 +49,28 @@
      * set to true the sufficiently-recent authentication token must indicate that the user
      * authenticated with a password, not a biometric.
      *
+     * Note that the IKeymasterDevice 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 enformcement logic in IKeymasterDevice.  So from the
+     * IKeymasterDevice 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 verificationToken is used by StrongBox implementations of IKeymasterDevice.  It
+     * provides the StrongBox IKeymasterDevice 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 verificationToken, the device is still considered to be locked.
+     * Crucially, if a StrongBox IKeymasterDevice receives a deviceLocked() call with a verification
+     * token timestamp that is less than the timestamp in the last deviceLocked() call, it must
+     * ignore the new timestamp.  TEE IKeymasterDevice implementations will receive an empty
+     * verificationToken (zero values and empty vectors) and should use their own clock as the
+     * device-lock time.
      */
-    deviceLocked(bool passwordOnly) generates (ErrorCode error);
+    deviceLocked(bool passwordOnly, VerificationToken verificationToken) generates (ErrorCode error);
 
     /**
      * Called by client to notify the IKeymasterDevice that the device has left the early boot
diff --git a/keymaster/4.1/default/Android.bp b/keymaster/4.1/default/Android.bp
index b06878b..27297b9 100644
--- a/keymaster/4.1/default/Android.bp
+++ b/keymaster/4.1/default/Android.bp
@@ -31,6 +31,7 @@
         "libhidlbase",
         "libkeymaster4",
         "libkeymaster41",
+        "libkeymaster4_1support",
         "liblog",
         "libutils",
     ],
diff --git a/keymaster/4.1/support/Android.bp b/keymaster/4.1/support/Android.bp
index 34b6108..bdd0ca8 100644
--- a/keymaster/4.1/support/Android.bp
+++ b/keymaster/4.1/support/Android.bp
@@ -22,11 +22,25 @@
         "-Wextra",
         "-Werror",
     ],
+    srcs: [
+        "attestation_record.cpp",
+        "Keymaster.cpp",
+        "Keymaster3.cpp",
+        "Keymaster4.cpp",
+    ],
     export_include_dirs: ["include"],
     shared_libs: [
         "android.hardware.keymaster@3.0",
         "android.hardware.keymaster@4.0",
         "android.hardware.keymaster@4.1",
+        "libbase",
+        "libcrypto",
+        "libhidlbase",
         "libkeymaster4support",
-    ]
+        "libutils",
+    ],
+    export_shared_lib_headers: [
+        "android.hardware.keymaster@4.1",
+        "libkeymaster4support",
+    ],
 }
diff --git a/keymaster/4.0/support/Keymaster.cpp b/keymaster/4.1/support/Keymaster.cpp
similarity index 85%
rename from keymaster/4.0/support/Keymaster.cpp
rename to keymaster/4.1/support/Keymaster.cpp
index f20f951..ea6604e 100644
--- a/keymaster/4.0/support/Keymaster.cpp
+++ b/keymaster/4.1/support/Keymaster.cpp
@@ -14,19 +14,18 @@
  ** limitations under the License.
  */
 
-#include <keymasterV4_0/Keymaster.h>
+#include <keymasterV4_1/Keymaster.h>
 
 #include <iomanip>
 
 #include <android-base/logging.h>
 #include <android/hidl/manager/1.2/IServiceManager.h>
-#include <keymasterV4_0/Keymaster3.h>
-#include <keymasterV4_0/Keymaster4.h>
 #include <keymasterV4_0/key_param_output.h>
 #include <keymasterV4_0/keymaster_utils.h>
+#include <keymasterV4_1/Keymaster3.h>
+#include <keymasterV4_1/Keymaster4.h>
 
-namespace android {
-namespace hardware {
+namespace android::hardware {
 
 template <class T>
 std::ostream& operator<<(std::ostream& os, const hidl_vec<T>& vec) {
@@ -57,6 +56,7 @@
 }
 
 namespace keymaster {
+
 namespace V4_0 {
 
 std::ostream& operator<<(std::ostream& os, const HmacSharingParameters& params) {
@@ -66,7 +66,9 @@
     return os;
 }
 
-namespace support {
+}  // namespace V4_0
+
+namespace V4_1::support {
 
 using ::android::sp;
 using ::android::hidl::manager::V1_2::IServiceManager;
@@ -140,14 +142,14 @@
 }
 
 static hidl_vec<HmacSharingParameters> getHmacParameters(
-    const Keymaster::KeymasterSet& keymasters) {
+        const Keymaster::KeymasterSet& keymasters) {
     std::vector<HmacSharingParameters> params_vec;
     params_vec.reserve(keymasters.size());
     for (auto& keymaster : keymasters) {
         if (keymaster->halVersion().majorVersion < 4) continue;
         auto rc = keymaster->getHmacSharingParameters([&](auto error, auto& params) {
-            CHECK(error == ErrorCode::OK)
-                << "Failed to get HMAC parameters from " << *keymaster << " error " << error;
+            CHECK(error == V4_0::ErrorCode::OK)
+                    << "Failed to get HMAC parameters from " << *keymaster << " error " << error;
             params_vec.push_back(params);
         });
         CHECK(rc.isOk()) << "Failed to communicate with " << *keymaster
@@ -169,18 +171,18 @@
         if (keymaster->halVersion().majorVersion < 4) continue;
         LOG(DEBUG) << "Computing HMAC for " << *keymaster;
         auto rc = keymaster->computeSharedHmac(
-            params, [&](ErrorCode error, const hidl_vec<uint8_t>& curSharingCheck) {
-                CHECK(error == ErrorCode::OK)
-                    << "Failed to get HMAC parameters from " << *keymaster << " error " << error;
-                if (firstKeymaster) {
-                    sharingCheck = curSharingCheck;
-                    firstKeymaster = false;
-                }
-                if (curSharingCheck != sharingCheck)
-                    LOG(WARNING) << "HMAC computation failed for " << *keymaster  //
-                                 << " Expected: " << sharingCheck                 //
-                                 << " got: " << curSharingCheck;
-            });
+                params, [&](V4_0::ErrorCode error, const hidl_vec<uint8_t>& curSharingCheck) {
+                    CHECK(error == V4_0::ErrorCode::OK) << "Failed to get HMAC parameters from "
+                                                        << *keymaster << " error " << error;
+                    if (firstKeymaster) {
+                        sharingCheck = curSharingCheck;
+                        firstKeymaster = false;
+                    }
+                    if (curSharingCheck != sharingCheck)
+                        LOG(WARNING) << "HMAC computation failed for " << *keymaster  //
+                                     << " Expected: " << sharingCheck                 //
+                                     << " got: " << curSharingCheck;
+                });
         CHECK(rc.isOk()) << "Failed to communicate with " << *keymaster
                          << " error: " << rc.description();
     }
@@ -190,8 +192,6 @@
     computeHmac(keymasters, getHmacParameters(keymasters));
 }
 
-}  // namespace support
-}  // namespace V4_0
+}  // namespace V4_1::support
 }  // namespace keymaster
-}  // namespace hardware
-}  // namespace android
+}  // namespace android::hardware
diff --git a/keymaster/4.0/support/Keymaster3.cpp b/keymaster/4.1/support/Keymaster3.cpp
similarity index 81%
rename from keymaster/4.0/support/Keymaster3.cpp
rename to keymaster/4.1/support/Keymaster3.cpp
index b2cdbd9..b665689 100644
--- a/keymaster/4.0/support/Keymaster3.cpp
+++ b/keymaster/4.1/support/Keymaster3.cpp
@@ -15,23 +15,19 @@
  ** limitations under the License.
  */
 
-#include <keymasterV4_0/Keymaster3.h>
+#include <keymasterV4_1/Keymaster3.h>
 
 #include <android-base/logging.h>
 #include <keymasterV4_0/keymaster_utils.h>
 
-namespace android {
-namespace hardware {
-namespace keymaster {
-namespace V4_0 {
-namespace support {
+namespace android::hardware::keymaster::V4_1::support {
 
 using android::hardware::details::StatusOf;
 
 namespace {
 
-ErrorCode convert(V3_0::ErrorCode error) {
-    return static_cast<ErrorCode>(error);
+V4_0::ErrorCode convert(V3_0::ErrorCode error) {
+    return static_cast<V4_0::ErrorCode>(error);
 }
 
 V3_0::KeyPurpose convert(KeyPurpose purpose) {
@@ -53,7 +49,7 @@
 
 KeyParameter convert(const V3_0::KeyParameter& param) {
     KeyParameter converted;
-    converted.tag = static_cast<Tag>(param.tag);
+    converted.tag = static_cast<V4_0::Tag>(param.tag);
     static_assert(sizeof(converted.f) == sizeof(param.f), "This function assumes sizes match");
     memcpy(&converted.f, &param.f, sizeof(param.f));
     converted.blob = param.blob;
@@ -89,7 +85,7 @@
         converted[i] = convert(params[i]);
     }
     converted[params.size()].tag = V3_0::Tag::AUTH_TOKEN;
-    converted[params.size()].blob = authToken2HidlVec(authToken);
+    converted[params.size()].blob = V4_0::support::authToken2HidlVec(authToken);
 
     return converted;
 }
@@ -107,16 +103,19 @@
     if (haveVersion_) return;
 
     auto rc = km3_dev_->getHardwareFeatures(
-        [&](bool isSecure, bool supportsEllipticCurve, bool supportsSymmetricCryptography,
-            bool supportsAttestation, bool supportsAllDigests, const hidl_string& keymasterName,
-            const hidl_string& keymasterAuthorName) {
-            version_ = {keymasterName, keymasterAuthorName, 0 /* major version, filled below */,
-                        isSecure ? SecurityLevel::TRUSTED_ENVIRONMENT : SecurityLevel::SOFTWARE,
-                        supportsEllipticCurve};
-            supportsSymmetricCryptography_ = supportsSymmetricCryptography;
-            supportsAttestation_ = supportsAttestation;
-            supportsAllDigests_ = supportsAllDigests;
-        });
+            [&](bool isSecure, bool supportsEllipticCurve, bool supportsSymmetricCryptography,
+                bool supportsAttestation, bool supportsAllDigests, const hidl_string& keymasterName,
+                const hidl_string& keymasterAuthorName) {
+                version_ = {keymasterName,
+                            keymasterAuthorName,
+                            0 /* major version, filled below */,
+                            0 /* minor version */,
+                            isSecure ? SecurityLevel::TRUSTED_ENVIRONMENT : SecurityLevel::SOFTWARE,
+                            supportsEllipticCurve};
+                supportsSymmetricCryptography_ = supportsSymmetricCryptography;
+                supportsAttestation_ = supportsAttestation;
+                supportsAllDigests_ = supportsAllDigests;
+            });
 
     CHECK(rc.isOk()) << "Got error " << rc.description() << " trying to get hardware features";
 
@@ -139,10 +138,10 @@
     return Void();
 }
 
-Return<ErrorCode> Keymaster3::addRngEntropy(const hidl_vec<uint8_t>& data) {
+Return<V4_0::ErrorCode> Keymaster3::addRngEntropy(const hidl_vec<uint8_t>& data) {
     auto rc = km3_dev_->addRngEntropy(data);
     if (!rc.isOk()) {
-        return StatusOf<V3_0::ErrorCode, ErrorCode>(rc);
+        return StatusOf<V3_0::ErrorCode, V4_0::ErrorCode>(rc);
     }
     return convert(rc);
 }
@@ -215,21 +214,21 @@
     return rc;
 }
 
-Return<ErrorCode> Keymaster3::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
+Return<V4_0::ErrorCode> Keymaster3::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
     auto rc = km3_dev_->deleteKey(keyBlob);
-    if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, ErrorCode>(rc);
+    if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, V4_0::ErrorCode>(rc);
     return convert(rc);
 }
 
-Return<ErrorCode> Keymaster3::deleteAllKeys() {
+Return<V4_0::ErrorCode> Keymaster3::deleteAllKeys() {
     auto rc = km3_dev_->deleteAllKeys();
-    if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, ErrorCode>(rc);
+    if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, V4_0::ErrorCode>(rc);
     return convert(rc);
 }
 
-Return<ErrorCode> Keymaster3::destroyAttestationIds() {
+Return<V4_0::ErrorCode> Keymaster3::destroyAttestationIds() {
     auto rc = km3_dev_->destroyAttestationIds();
-    if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, ErrorCode>(rc);
+    if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, V4_0::ErrorCode>(rc);
     return convert(rc);
 }
 
@@ -242,7 +241,7 @@
     };
 
     auto rc =
-        km3_dev_->begin(convert(purpose), key, convertAndAddAuthToken(inParams, authToken), cb);
+            km3_dev_->begin(convert(purpose), key, convertAndAddAuthToken(inParams, authToken), cb);
     rc.isOk();  // move ctor prereq
     return rc;
 }
@@ -256,8 +255,8 @@
         _hidl_cb(convert(error), inputConsumed, convert(outParams), output);
     };
 
-    auto rc =
-        km3_dev_->update(operationHandle, convertAndAddAuthToken(inParams, authToken), input, cb);
+    auto rc = km3_dev_->update(operationHandle, convertAndAddAuthToken(inParams, authToken), input,
+                               cb);
     rc.isOk();  // move ctor prereq
     return rc;
 }
@@ -278,14 +277,10 @@
     return rc;
 }
 
-Return<ErrorCode> Keymaster3::abort(uint64_t operationHandle) {
+Return<V4_0::ErrorCode> Keymaster3::abort(uint64_t operationHandle) {
     auto rc = km3_dev_->abort(operationHandle);
-    if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, ErrorCode>(rc);
+    if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, V4_0::ErrorCode>(rc);
     return convert(rc);
 }
 
-}  // namespace support
-}  // namespace V4_0
-}  // namespace keymaster
-}  // namespace hardware
-}  // namespace android
+}  // namespace android::hardware::keymaster::V4_1::support
diff --git a/keymaster/4.1/support/Keymaster4.cpp b/keymaster/4.1/support/Keymaster4.cpp
new file mode 100644
index 0000000..33f4bb1
--- /dev/null
+++ b/keymaster/4.1/support/Keymaster4.cpp
@@ -0,0 +1,41 @@
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <keymasterV4_1/Keymaster4.h>
+
+#include <android-base/logging.h>
+
+namespace android::hardware::keymaster::V4_1::support {
+
+void Keymaster4::getVersionIfNeeded() {
+    if (haveVersion_) return;
+
+    auto rc = km4_0_dev_->getHardwareInfo([&](SecurityLevel securityLevel,
+                                              const hidl_string& keymasterName,
+                                              const hidl_string& authorName) {
+        version_ = {keymasterName,
+                    authorName,
+                    4 /* major version */,
+                    static_cast<uint8_t>((km4_1_dev_) ? 1 : 0) /* minor version */,
+                    securityLevel,
+                    true /* supportsEc */};
+        haveVersion_ = true;
+    });
+
+    CHECK(rc.isOk()) << "Got error " << rc.description() << " trying to get hardware info";
+}
+
+}  // namespace android::hardware::keymaster::V4_1::support
diff --git a/keymaster/4.1/support/OWNERS b/keymaster/4.1/support/OWNERS
new file mode 100644
index 0000000..a9efe66
--- /dev/null
+++ b/keymaster/4.1/support/OWNERS
@@ -0,0 +1,3 @@
+jdanis@google.com
+swillden@google.com
+jbires@google.com
diff --git a/keymaster/4.1/support/attestation_record.cpp b/keymaster/4.1/support/attestation_record.cpp
new file mode 100644
index 0000000..9eab1db
--- /dev/null
+++ b/keymaster/4.1/support/attestation_record.cpp
@@ -0,0 +1,387 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <keymasterV4_1/attestation_record.h>
+
+#include <android-base/logging.h>
+#include <assert.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include <keymasterV4_0/authorization_set.h>
+#include <keymasterV4_0/openssl_utils.h>
+
+#define AT __FILE__ ":" << __LINE__
+
+/*
+ * NOTE: The contents of this file are *extremely* similar to the contents of the V4_0 copy of the
+ * same support file.  Unfortunately, small changes in the scheme mean that the schema types have to
+ * be distinct, which drives almost everything else to be different as well.  In the next version we
+ * plan to abandon not just this openssl mechanism for parsing ASN.1, but ASN.1 entirely, so
+ * eventually all of this duplication can be removed.
+ */
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V4_1 {
+
+struct stack_st_ASN1_TYPE_Delete {
+    void operator()(stack_st_ASN1_TYPE* p) { sk_ASN1_TYPE_free(p); }
+};
+
+struct ASN1_STRING_Delete {
+    void operator()(ASN1_STRING* p) { ASN1_STRING_free(p); }
+};
+
+struct ASN1_TYPE_Delete {
+    void operator()(ASN1_TYPE* p) { ASN1_TYPE_free(p); }
+};
+
+#define ASN1_INTEGER_SET STACK_OF(ASN1_INTEGER)
+
+typedef struct km_root_of_trust {
+    ASN1_OCTET_STRING* verified_boot_key;
+    ASN1_BOOLEAN* device_locked;
+    ASN1_ENUMERATED* verified_boot_state;
+    ASN1_OCTET_STRING* verified_boot_hash;
+} KM_ROOT_OF_TRUST;
+
+ASN1_SEQUENCE(KM_ROOT_OF_TRUST) = {
+        ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_key, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(KM_ROOT_OF_TRUST, device_locked, ASN1_BOOLEAN),
+        ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_state, ASN1_ENUMERATED),
+        ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_hash, ASN1_OCTET_STRING),
+} ASN1_SEQUENCE_END(KM_ROOT_OF_TRUST);
+IMPLEMENT_ASN1_FUNCTIONS(KM_ROOT_OF_TRUST);
+
+typedef struct km_auth_list {
+    ASN1_INTEGER_SET* purpose;
+    ASN1_INTEGER* algorithm;
+    ASN1_INTEGER* key_size;
+    ASN1_INTEGER_SET* digest;
+    ASN1_INTEGER_SET* padding;
+    ASN1_INTEGER* ec_curve;
+    ASN1_INTEGER* rsa_public_exponent;
+    ASN1_INTEGER* active_date_time;
+    ASN1_INTEGER* origination_expire_date_time;
+    ASN1_INTEGER* usage_expire_date_time;
+    ASN1_NULL* no_auth_required;
+    ASN1_INTEGER* user_auth_type;
+    ASN1_INTEGER* auth_timeout;
+    ASN1_NULL* allow_while_on_body;
+    ASN1_NULL* all_applications;
+    ASN1_OCTET_STRING* application_id;
+    ASN1_INTEGER* creation_date_time;
+    ASN1_INTEGER* origin;
+    ASN1_NULL* rollback_resistance;
+    KM_ROOT_OF_TRUST* root_of_trust;
+    ASN1_INTEGER* os_version;
+    ASN1_INTEGER* os_patchlevel;
+    ASN1_OCTET_STRING* attestation_application_id;
+    ASN1_NULL* trusted_user_presence_required;
+    ASN1_NULL* trusted_confirmation_required;
+    ASN1_NULL* unlocked_device_required;
+    ASN1_INTEGER* vendor_patchlevel;
+    ASN1_INTEGER* boot_patchlevel;
+    ASN1_NULL* early_boot_only;
+    ASN1_NULL* device_unique_attestation;
+} KM_AUTH_LIST;
+
+ASN1_SEQUENCE(KM_AUTH_LIST) = {
+        ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, purpose, ASN1_INTEGER, TAG_PURPOSE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, algorithm, ASN1_INTEGER, TAG_ALGORITHM.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, key_size, ASN1_INTEGER, TAG_KEY_SIZE.maskedTag()),
+        ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, digest, ASN1_INTEGER, TAG_DIGEST.maskedTag()),
+        ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, padding, ASN1_INTEGER, TAG_PADDING.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, ec_curve, ASN1_INTEGER, TAG_EC_CURVE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, rsa_public_exponent, ASN1_INTEGER,
+                     TAG_RSA_PUBLIC_EXPONENT.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, rollback_resistance, ASN1_NULL,
+                     TAG_ROLLBACK_RESISTANCE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, active_date_time, ASN1_INTEGER, TAG_ACTIVE_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, origination_expire_date_time, ASN1_INTEGER,
+                     TAG_ORIGINATION_EXPIRE_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, usage_expire_date_time, ASN1_INTEGER,
+                     TAG_USAGE_EXPIRE_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, no_auth_required, ASN1_NULL, TAG_NO_AUTH_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, user_auth_type, ASN1_INTEGER, TAG_USER_AUTH_TYPE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, auth_timeout, ASN1_INTEGER, TAG_AUTH_TIMEOUT.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, allow_while_on_body, ASN1_NULL,
+                     TAG_ALLOW_WHILE_ON_BODY.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, trusted_user_presence_required, ASN1_NULL,
+                     TAG_TRUSTED_USER_PRESENCE_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, trusted_confirmation_required, ASN1_NULL,
+                     TAG_TRUSTED_CONFIRMATION_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, unlocked_device_required, ASN1_NULL,
+                     TAG_UNLOCKED_DEVICE_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, creation_date_time, ASN1_INTEGER,
+                     TAG_CREATION_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, origin, ASN1_INTEGER, TAG_ORIGIN.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, root_of_trust, KM_ROOT_OF_TRUST, TAG_ROOT_OF_TRUST.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, os_version, ASN1_INTEGER, TAG_OS_VERSION.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, os_patchlevel, ASN1_INTEGER, TAG_OS_PATCHLEVEL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, vendor_patchlevel, ASN1_INTEGER,
+                     TAG_VENDOR_PATCHLEVEL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, boot_patchlevel, ASN1_INTEGER, TAG_BOOT_PATCHLEVEL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_application_id, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_APPLICATION_ID.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, early_boot_only, ASN1_NULL, TAG_EARLY_BOOT_ONLY.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, device_unique_attestation, ASN1_NULL,
+                     TAG_DEVICE_UNIQUE_ATTESTATION.maskedTag()),
+} ASN1_SEQUENCE_END(KM_AUTH_LIST);
+IMPLEMENT_ASN1_FUNCTIONS(KM_AUTH_LIST);
+
+typedef struct km_key_description {
+    ASN1_INTEGER* attestation_version;
+    ASN1_ENUMERATED* attestation_security_level;
+    ASN1_INTEGER* keymaster_version;
+    ASN1_ENUMERATED* keymaster_security_level;
+    ASN1_OCTET_STRING* attestation_challenge;
+    KM_AUTH_LIST* software_enforced;
+    KM_AUTH_LIST* tee_enforced;
+    ASN1_INTEGER* unique_id;
+} KM_KEY_DESCRIPTION;
+
+ASN1_SEQUENCE(KM_KEY_DESCRIPTION) = {
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_version, ASN1_INTEGER),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_security_level, ASN1_ENUMERATED),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, keymaster_version, ASN1_INTEGER),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, keymaster_security_level, ASN1_ENUMERATED),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_challenge, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, unique_id, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, software_enforced, KM_AUTH_LIST),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, tee_enforced, KM_AUTH_LIST),
+} ASN1_SEQUENCE_END(KM_KEY_DESCRIPTION);
+IMPLEMENT_ASN1_FUNCTIONS(KM_KEY_DESCRIPTION);
+
+template <V4_0::Tag tag>
+void copyAuthTag(const stack_st_ASN1_INTEGER* stack, TypedTag<TagType::ENUM_REP, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    typedef typename V4_0::TypedTag2ValueType<decltype(ttag)>::type ValueT;
+    for (size_t i = 0; i < sk_ASN1_INTEGER_num(stack); ++i) {
+        auth_list->push_back(
+                ttag, static_cast<ValueT>(ASN1_INTEGER_get(sk_ASN1_INTEGER_value(stack, i))));
+    }
+}
+
+template <V4_0::Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::ENUM, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    typedef typename V4_0::TypedTag2ValueType<decltype(ttag)>::type ValueT;
+    if (!asn1_int) return;
+    auth_list->push_back(ttag, static_cast<ValueT>(ASN1_INTEGER_get(asn1_int)));
+}
+
+template <V4_0::Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::UINT, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    if (!asn1_int) return;
+    auth_list->push_back(ttag, ASN1_INTEGER_get(asn1_int));
+}
+
+BIGNUM* construct_uint_max() {
+    BIGNUM* value = BN_new();
+    BIGNUM_Ptr one(BN_new());
+    BN_one(one.get());
+    BN_lshift(value, one.get(), 32);
+    return value;
+}
+
+uint64_t BignumToUint64(BIGNUM* num) {
+    static_assert((sizeof(BN_ULONG) == sizeof(uint32_t)) || (sizeof(BN_ULONG) == sizeof(uint64_t)),
+                  "This implementation only supports 32 and 64-bit BN_ULONG");
+    if (sizeof(BN_ULONG) == sizeof(uint32_t)) {
+        BIGNUM_Ptr uint_max(construct_uint_max());
+        BIGNUM_Ptr hi(BN_new()), lo(BN_new());
+        BN_CTX_Ptr ctx(BN_CTX_new());
+        BN_div(hi.get(), lo.get(), num, uint_max.get(), ctx.get());
+        return static_cast<uint64_t>(BN_get_word(hi.get())) << 32 | BN_get_word(lo.get());
+    } else if (sizeof(BN_ULONG) == sizeof(uint64_t)) {
+        return BN_get_word(num);
+    } else {
+        return 0;
+    }
+}
+
+template <V4_0::Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::ULONG, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    if (!asn1_int) return;
+    BIGNUM_Ptr num(ASN1_INTEGER_to_BN(asn1_int, nullptr));
+    auth_list->push_back(ttag, BignumToUint64(num.get()));
+}
+
+template <V4_0::Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::DATE, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    if (!asn1_int) return;
+    BIGNUM_Ptr num(ASN1_INTEGER_to_BN(asn1_int, nullptr));
+    auth_list->push_back(ttag, BignumToUint64(num.get()));
+}
+
+template <V4_0::Tag tag>
+void copyAuthTag(const ASN1_NULL* asn1_null, TypedTag<TagType::BOOL, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    if (!asn1_null) return;
+    auth_list->push_back(ttag);
+}
+
+template <V4_0::Tag tag>
+void copyAuthTag(const ASN1_OCTET_STRING* asn1_string, TypedTag<TagType::BYTES, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    if (!asn1_string) return;
+    hidl_vec<uint8_t> buf;
+    buf.setToExternal(asn1_string->data, asn1_string->length);
+    auth_list->push_back(ttag, buf);
+}
+
+// Extract the values from the specified ASN.1 record and place them in auth_list.
+static ErrorCode extract_auth_list(const KM_AUTH_LIST* record, AuthorizationSet* auth_list) {
+    if (!record) return ErrorCode::OK;
+
+    copyAuthTag(record->active_date_time, TAG_ACTIVE_DATETIME, auth_list);
+    copyAuthTag(record->algorithm, TAG_ALGORITHM, auth_list);
+    copyAuthTag(record->application_id, TAG_APPLICATION_ID, auth_list);
+    copyAuthTag(record->auth_timeout, TAG_AUTH_TIMEOUT, auth_list);
+    copyAuthTag(record->creation_date_time, TAG_CREATION_DATETIME, auth_list);
+    copyAuthTag(record->digest, TAG_DIGEST, auth_list);
+    copyAuthTag(record->ec_curve, TAG_EC_CURVE, auth_list);
+    copyAuthTag(record->key_size, TAG_KEY_SIZE, auth_list);
+    copyAuthTag(record->no_auth_required, TAG_NO_AUTH_REQUIRED, auth_list);
+    copyAuthTag(record->origin, TAG_ORIGIN, auth_list);
+    copyAuthTag(record->origination_expire_date_time, TAG_ORIGINATION_EXPIRE_DATETIME, auth_list);
+    copyAuthTag(record->os_patchlevel, TAG_OS_PATCHLEVEL, auth_list);
+    copyAuthTag(record->os_version, TAG_OS_VERSION, auth_list);
+    copyAuthTag(record->padding, TAG_PADDING, auth_list);
+    copyAuthTag(record->purpose, TAG_PURPOSE, auth_list);
+    copyAuthTag(record->rollback_resistance, TAG_ROLLBACK_RESISTANCE, auth_list);
+    copyAuthTag(record->rsa_public_exponent, TAG_RSA_PUBLIC_EXPONENT, auth_list);
+    copyAuthTag(record->usage_expire_date_time, TAG_USAGE_EXPIRE_DATETIME, auth_list);
+    copyAuthTag(record->user_auth_type, TAG_USER_AUTH_TYPE, auth_list);
+    copyAuthTag(record->attestation_application_id, TAG_ATTESTATION_APPLICATION_ID, auth_list);
+    copyAuthTag(record->vendor_patchlevel, TAG_VENDOR_PATCHLEVEL, auth_list);
+    copyAuthTag(record->boot_patchlevel, TAG_BOOT_PATCHLEVEL, auth_list);
+    copyAuthTag(record->trusted_user_presence_required, TAG_TRUSTED_USER_PRESENCE_REQUIRED,
+                auth_list);
+    copyAuthTag(record->trusted_confirmation_required, TAG_TRUSTED_CONFIRMATION_REQUIRED,
+                auth_list);
+    copyAuthTag(record->unlocked_device_required, TAG_UNLOCKED_DEVICE_REQUIRED, auth_list);
+    copyAuthTag(record->early_boot_only, TAG_EARLY_BOOT_ONLY, auth_list);
+    copyAuthTag(record->device_unique_attestation, TAG_DEVICE_UNIQUE_ATTESTATION, auth_list);
+
+    return ErrorCode::OK;
+}
+
+MAKE_OPENSSL_PTR_TYPE(KM_KEY_DESCRIPTION)
+
+// Parse the DER-encoded attestation record, placing the results in keymaster_version,
+// attestation_challenge, software_enforced, tee_enforced and unique_id.
+std::tuple<ErrorCode, AttestationRecord> parse_attestation_record(const hidl_vec<uint8_t>& cert) {
+    const uint8_t* p = cert.data();
+    X509_Ptr x509(d2i_X509(nullptr, &p, cert.size()));
+
+    ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1 /* dotted string format */));
+    if (!oid.get()) {
+        LOG(ERROR) << "Error parsing OID";
+        return {ErrorCode::UNKNOWN_ERROR, {}};
+    }
+
+    int location = X509_get_ext_by_OBJ(x509.get(), oid.get(), -1 /* search from beginning */);
+    if (location == -1) {
+        LOG(ERROR) << "Attestation extension not found in certificate";
+        return {ErrorCode::UNKNOWN_ERROR, {}};
+    }
+
+    X509_EXTENSION* attest_rec_ext = X509_get_ext(x509.get(), location);
+    if (!attest_rec_ext) {
+        LOG(ERROR) << "Found extension but couldn't retrieve it.  Probably BoringSSL bug.";
+        return {ErrorCode::UNKNOWN_ERROR, {}};
+    }
+
+    ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext);
+    if (!attest_rec_ext) {
+        LOG(ERROR) << "Attestation extension contained no data";
+        return {ErrorCode::UNKNOWN_ERROR, {}};
+    }
+
+    p = attest_rec->data;
+    KM_KEY_DESCRIPTION_Ptr record(d2i_KM_KEY_DESCRIPTION(nullptr, &p, attest_rec->length));
+    if (!record.get()) return {ErrorCode::UNKNOWN_ERROR, {}};
+
+    AttestationRecord result;
+
+    result.attestation_version = ASN1_INTEGER_get(record->attestation_version);
+    result.attestation_security_level =
+            static_cast<SecurityLevel>(ASN1_ENUMERATED_get(record->attestation_security_level));
+    result.keymaster_version = ASN1_INTEGER_get(record->keymaster_version);
+    result.keymaster_security_level =
+            static_cast<SecurityLevel>(ASN1_ENUMERATED_get(record->keymaster_security_level));
+
+    auto& chall = record->attestation_challenge;
+    result.attestation_challenge.resize(chall->length);
+    memcpy(result.attestation_challenge.data(), chall->data, chall->length);
+    auto& uid = record->unique_id;
+    result.unique_id.resize(uid->length);
+    memcpy(result.unique_id.data(), uid->data, uid->length);
+
+    ErrorCode error = extract_auth_list(record->software_enforced, &result.software_enforced);
+    if (error != ErrorCode::OK) return {error, {}};
+
+    error = extract_auth_list(record->tee_enforced, &result.hardware_enforced);
+    if (error != ErrorCode::OK) return {error, {}};
+
+    KM_ROOT_OF_TRUST* root_of_trust = nullptr;
+    if (record->tee_enforced && record->tee_enforced->root_of_trust) {
+        root_of_trust = record->tee_enforced->root_of_trust;
+    } else if (record->software_enforced && record->software_enforced->root_of_trust) {
+        root_of_trust = record->software_enforced->root_of_trust;
+    } else {
+        LOG(ERROR) << AT << " Failed root of trust parsing";
+        return {ErrorCode::INVALID_ARGUMENT, {}};
+    }
+    if (!root_of_trust->verified_boot_key) {
+        LOG(ERROR) << AT << " Failed verified boot key parsing";
+        return {ErrorCode::INVALID_ARGUMENT, {}};
+    }
+
+    RootOfTrust& rot = result.root_of_trust;
+    auto& vb_key = root_of_trust->verified_boot_key;
+    rot.verified_boot_key.resize(vb_key->length);
+    memcpy(rot.verified_boot_key.data(), vb_key->data, vb_key->length);
+
+    rot.verified_boot_state = static_cast<keymaster_verified_boot_t>(
+            ASN1_ENUMERATED_get(root_of_trust->verified_boot_state));
+    rot.device_locked = root_of_trust->device_locked;
+
+    auto& vb_hash = root_of_trust->verified_boot_hash;
+    if (!vb_hash) {
+        LOG(ERROR) << AT << " Failed verified boot hash parsing";
+        return {ErrorCode::INVALID_ARGUMENT, {}};
+    }
+    rot.verified_boot_hash.resize(vb_hash->length);
+    memcpy(rot.verified_boot_hash.data(), vb_hash->data, vb_hash->length);
+
+    return {ErrorCode::OK, result};
+}
+
+}  // namespace V4_1
+}  // namespace keymaster
+}  // namespace hardware
+}  // namespace android
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h b/keymaster/4.1/support/include/keymasterV4_1/Keymaster.h
similarity index 73%
rename from keymaster/4.0/support/include/keymasterV4_0/Keymaster.h
rename to keymaster/4.1/support/include/keymasterV4_1/Keymaster.h
index ad83f17..1f49e18 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h
+++ b/keymaster/4.1/support/include/keymasterV4_1/Keymaster.h
@@ -15,28 +15,28 @@
  ** limitations under the License.
  */
 
-#ifndef HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_H_
-#define HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_H_
-
-#include <android/hardware/keymaster/4.0/IKeymasterDevice.h>
+#pragma once
 
 #include <memory>
 #include <vector>
 
-namespace android {
-namespace hardware {
-namespace keymaster {
-namespace V4_0 {
-namespace support {
+#include <android/hardware/keymaster/4.1/IKeymasterDevice.h>
+#include <keymasterV4_1/keymaster_tags.h>
+
+namespace android::hardware::keymaster::V4_1::support {
 
 /**
- * Keymaster abstracts the underlying V4_0::IKeymasterDevice.  There is one implementation
- * (Keymaster4) which is a trivial passthrough and one that wraps a V3_0::IKeymasterDevice.
+ * Keymaster abstracts the underlying V4_1::IKeymasterDevice.  There are two implementations,
+ * Keymaster3 which wraps a V3_0::IKeymasterDevice and Keymaster4, which wraps either a
+ * V4_0::IKeymasterDevice or a V4_1::IKeymasterDevice.  There is a V3_0::IKeymasterDevice
+ * implementation that is used to wrap pre-HIDL keymaster implementations, and Keymaster3 will wrap
+ * that.
  *
  * The reason for adding this additional layer, rather than simply using the latest HAL directly and
  * subclassing it to wrap any older HAL, is because this provides a place to put additional methods
  * which clients can use when they need to distinguish between different underlying HAL versions,
- * while still having to use only the latest interface.
+ * while still having to use only the latest interface.  Plus it's a handy place to keep some
+ * convenience methods.
  */
 class Keymaster : public IKeymasterDevice {
   public:
@@ -50,12 +50,14 @@
         hidl_string keymasterName;
         hidl_string authorName;
         uint8_t majorVersion;
+        uint8_t minorVersion;
         SecurityLevel securityLevel;
         bool supportsEc;
 
         bool operator>(const VersionResult& other) const {
-            auto lhs = std::tie(securityLevel, majorVersion, supportsEc);
-            auto rhs = std::tie(other.securityLevel, other.majorVersion, other.supportsEc);
+            auto lhs = std::tie(securityLevel, majorVersion, minorVersion, supportsEc);
+            auto rhs = std::tie(other.securityLevel, other.majorVersion, other.minorVersion,
+                                other.supportsEc);
             return lhs > rhs;
         }
     };
@@ -69,6 +71,9 @@
      * There are no side effects otherwise.
      */
     void logIfKeymasterVendorError(ErrorCode ec) const;
+    void logIfKeymasterVendorError(V4_0::ErrorCode ec) const {
+        logIfKeymasterVendorError(static_cast<ErrorCode>(ec));
+    }
 
     /**
      * Returns all available Keymaster3 and Keymaster4 instances, in order of most secure to least
@@ -93,10 +98,4 @@
 
 std::ostream& operator<<(std::ostream& os, const Keymaster& keymaster);
 
-}  // namespace support
-}  // namespace V4_0
-}  // namespace keymaster
-}  // namespace hardware
-}  // namespace android
-
-#endif  // HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_H_
+}  // namespace android::hardware::keymaster::V4_1::support
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h b/keymaster/4.1/support/include/keymasterV4_1/Keymaster3.h
similarity index 72%
rename from keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
rename to keymaster/4.1/support/include/keymasterV4_1/Keymaster3.h
index c40be7c..c201e8c 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
+++ b/keymaster/4.1/support/include/keymasterV4_1/Keymaster3.h
@@ -1,5 +1,4 @@
 /*
- **
  ** Copyright 2017, The Android Open Source Project
  **
  ** Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,18 +14,14 @@
  ** limitations under the License.
  */
 
-#ifndef HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_3_H_
-#define HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_3_H_
+#pragma once
 
 #include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
 
 #include "Keymaster.h"
+#include "Operation.h"
 
-namespace android {
-namespace hardware {
-namespace keymaster {
-namespace V4_0 {
-namespace support {
+namespace android::hardware::keymaster::V4_1::support {
 
 using IKeymaster3Device = ::android::hardware::keymaster::V3_0::IKeymasterDevice;
 
@@ -38,8 +33,10 @@
 using ::android::hardware::details::return_status;
 
 class Keymaster3 : public Keymaster {
-   public:
+  public:
+    // This definition is used for device enumeration.
     using WrappedIKeymasterDevice = IKeymaster3Device;
+
     Keymaster3(sp<IKeymaster3Device> km3_dev, const hidl_string& instanceName)
         : Keymaster(IKeymaster3Device::descriptor, instanceName),
           km3_dev_(km3_dev),
@@ -53,24 +50,24 @@
     Return<void> getHardwareInfo(getHardwareInfo_cb _hidl_cb);
 
     Return<void> getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) override {
-        _hidl_cb(ErrorCode::UNIMPLEMENTED, {});
+        _hidl_cb(V4_0::ErrorCode::UNIMPLEMENTED, {});
         return Void();
     }
 
     Return<void> computeSharedHmac(const hidl_vec<HmacSharingParameters>&,
                                    computeSharedHmac_cb _hidl_cb) override {
-        _hidl_cb(ErrorCode::UNIMPLEMENTED, {});
+        _hidl_cb(V4_0::ErrorCode::UNIMPLEMENTED, {});
         return Void();
     }
 
     Return<void> verifyAuthorization(uint64_t, const hidl_vec<KeyParameter>&,
                                      const HardwareAuthToken&,
                                      verifyAuthorization_cb _hidl_cb) override {
-        _hidl_cb(ErrorCode::UNIMPLEMENTED, {});
+        _hidl_cb(V4_0::ErrorCode::UNIMPLEMENTED, {});
         return Void();
     }
 
-    Return<ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override;
+    Return<V4_0::ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override;
     Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
                              generateKey_cb _hidl_cb) override;
     Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
@@ -86,7 +83,7 @@
                                   const hidl_vec<KeyParameter>& /* unwrappingParams */,
                                   uint64_t /* passwordSid */, uint64_t /* biometricSid */,
                                   importWrappedKey_cb _hidl_cb) {
-        _hidl_cb(ErrorCode::UNIMPLEMENTED, {}, {});
+        _hidl_cb(V4_0::ErrorCode::UNIMPLEMENTED, {}, {});
         return Void();
     }
 
@@ -99,9 +96,9 @@
     Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
                             const hidl_vec<KeyParameter>& upgradeParams,
                             upgradeKey_cb _hidl_cb) override;
-    Return<ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override;
-    Return<ErrorCode> deleteAllKeys() override;
-    Return<ErrorCode> destroyAttestationIds() override;
+    Return<V4_0::ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override;
+    Return<V4_0::ErrorCode> deleteAllKeys() override;
+    Return<V4_0::ErrorCode> destroyAttestationIds() override;
     Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
                        const hidl_vec<KeyParameter>& inParams, const HardwareAuthToken& authToken,
                        begin_cb _hidl_cb) override;
@@ -112,9 +109,31 @@
                         const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,
                         const HardwareAuthToken& authToken,
                         const VerificationToken& verificationToken, finish_cb _hidl_cb) override;
-    Return<ErrorCode> abort(uint64_t operationHandle) override;
+    Return<V4_0::ErrorCode> abort(uint64_t operationHandle) override;
 
-   private:
+    /**********************************
+     * V4_1::IKeymasterDevice methods *
+     *********************************/
+
+    Return<ErrorCode> deviceLocked(bool /* passwordOnly */,
+                                   const VerificationToken& /* verificationToken */) override {
+        return ErrorCode::UNIMPLEMENTED;
+    }
+
+    Return<ErrorCode> earlyBootEnded() override { return ErrorCode::UNIMPLEMENTED; }
+
+    Return<void> beginOp(KeyPurpose purpose, const hidl_vec<uint8_t>& keyBlob,
+                         const hidl_vec<KeyParameter>& inParams, const HardwareAuthToken& authToken,
+                         beginOp_cb _hidl_cb) override {
+        return begin(purpose, keyBlob, inParams, authToken,
+                     [&_hidl_cb](V4_0::ErrorCode errorCode, const hidl_vec<KeyParameter>& outParams,
+                                 OperationHandle operationHandle) {
+                         _hidl_cb(static_cast<ErrorCode>(errorCode), outParams,
+                                  new Operation(operationHandle));
+                     });
+    }
+
+  private:
     void getVersionIfNeeded();
 
     sp<IKeymaster3Device> km3_dev_;
@@ -126,10 +145,4 @@
     bool supportsAllDigests_;
 };
 
-}  // namespace support
-}  // namespace V4_0
-}  // namespace keymaster
-}  // namespace hardware
-}  // namespace android
-
-#endif  // HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_3_H_
+}  // namespace android::hardware::keymaster::V4_1::support
diff --git a/keymaster/4.1/support/include/keymasterV4_1/Keymaster4.h b/keymaster/4.1/support/include/keymasterV4_1/Keymaster4.h
new file mode 100644
index 0000000..6d74d98
--- /dev/null
+++ b/keymaster/4.1/support/include/keymasterV4_1/Keymaster4.h
@@ -0,0 +1,197 @@
+/*
+ ** 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 "Keymaster.h"
+#include "Operation.h"
+
+namespace android::hardware::keymaster::V4_1::support {
+
+using android::sp;
+
+/**
+ * This class can wrap either a V4_0 or V4_1 IKeymasterDevice.
+ */
+class Keymaster4 : public Keymaster {
+  public:
+    // This definition is used for device enumeration; enumerating 4.0 devices will also
+    // enumerate 4.1. devices.
+    using WrappedIKeymasterDevice = V4_0::IKeymasterDevice;
+
+    Keymaster4(sp<V4_1::IKeymasterDevice> km4_1_dev, const hidl_string& instanceName)
+        : Keymaster(V4_1::IKeymasterDevice::descriptor, instanceName),
+          haveVersion_(false),
+          km4_0_dev_(km4_1_dev),
+          km4_1_dev_(km4_1_dev) {}
+
+    Keymaster4(sp<V4_0::IKeymasterDevice> km4_0_dev, const hidl_string& instanceName)
+        : Keymaster(V4_1::IKeymasterDevice::descriptor, instanceName),
+          haveVersion_(false),
+          km4_0_dev_(km4_0_dev),
+          km4_1_dev_() {}
+
+    const VersionResult& halVersion() const override {
+        const_cast<Keymaster4*>(this)->getVersionIfNeeded();
+        return version_;
+    }
+
+    /**********************************
+     * V4_0::IKeymasterDevice methods *
+     *********************************/
+
+    Return<void> getHardwareInfo(getHardwareInfo_cb _hidl_cb) override {
+        return km4_0_dev_->getHardwareInfo(_hidl_cb);
+    }
+
+    Return<void> getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) override {
+        return km4_0_dev_->getHmacSharingParameters(_hidl_cb);
+    }
+
+    Return<void> computeSharedHmac(const hidl_vec<HmacSharingParameters>& params,
+                                   computeSharedHmac_cb _hidl_cb) override {
+        return km4_0_dev_->computeSharedHmac(params, _hidl_cb);
+    }
+
+    Return<void> verifyAuthorization(uint64_t operationHandle, const hidl_vec<KeyParameter>& params,
+                                     const HardwareAuthToken& authToken,
+                                     verifyAuthorization_cb _hidl_cb) override {
+        return km4_0_dev_->verifyAuthorization(operationHandle, params, authToken, _hidl_cb);
+    }
+
+    Return<V4_0::ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override {
+        return km4_0_dev_->addRngEntropy(data);
+    }
+
+    Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
+                             generateKey_cb _hidl_cb) override {
+        return km4_0_dev_->generateKey(keyParams, _hidl_cb);
+    }
+
+    Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
+                                       const hidl_vec<uint8_t>& clientId,
+                                       const hidl_vec<uint8_t>& appData,
+                                       getKeyCharacteristics_cb _hidl_cb) override {
+        return km4_0_dev_->getKeyCharacteristics(keyBlob, clientId, appData, _hidl_cb);
+    }
+
+    Return<void> importKey(const hidl_vec<KeyParameter>& params, KeyFormat keyFormat,
+                           const hidl_vec<uint8_t>& keyData, importKey_cb _hidl_cb) override {
+        return km4_0_dev_->importKey(params, keyFormat, keyData, _hidl_cb);
+    }
+
+    Return<void> importWrappedKey(const hidl_vec<uint8_t>& wrappedKeyData,
+                                  const hidl_vec<uint8_t>& wrappingKeyBlob,
+                                  const hidl_vec<uint8_t>& maskingKey,
+                                  const hidl_vec<KeyParameter>& unwrappingParams,
+                                  uint64_t passwordSid, uint64_t biometricSid,
+                                  importWrappedKey_cb _hidl_cb) {
+        return km4_0_dev_->importWrappedKey(wrappedKeyData, wrappingKeyBlob, maskingKey,
+                                            unwrappingParams, passwordSid, biometricSid, _hidl_cb);
+    }
+
+    Return<void> exportKey(KeyFormat exportFormat, const hidl_vec<uint8_t>& keyBlob,
+                           const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
+                           exportKey_cb _hidl_cb) override {
+        return km4_0_dev_->exportKey(exportFormat, keyBlob, clientId, appData, _hidl_cb);
+    }
+
+    Return<void> attestKey(const hidl_vec<uint8_t>& keyToAttest,
+                           const hidl_vec<KeyParameter>& attestParams,
+                           attestKey_cb _hidl_cb) override {
+        return km4_0_dev_->attestKey(keyToAttest, attestParams, _hidl_cb);
+    }
+
+    Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
+                            const hidl_vec<KeyParameter>& upgradeParams,
+                            upgradeKey_cb _hidl_cb) override {
+        return km4_0_dev_->upgradeKey(keyBlobToUpgrade, upgradeParams, _hidl_cb);
+    }
+
+    Return<V4_0::ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override {
+        return km4_0_dev_->deleteKey(keyBlob);
+    }
+
+    Return<V4_0::ErrorCode> deleteAllKeys() override { return km4_0_dev_->deleteAllKeys(); }
+
+    Return<V4_0::ErrorCode> destroyAttestationIds() override {
+        return km4_0_dev_->destroyAttestationIds();
+    }
+
+    Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
+                       const hidl_vec<KeyParameter>& inParams, const HardwareAuthToken& authToken,
+                       begin_cb _hidl_cb) override {
+        return km4_0_dev_->begin(purpose, key, inParams, authToken, _hidl_cb);
+    }
+
+    Return<void> update(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+                        const hidl_vec<uint8_t>& input, const HardwareAuthToken& authToken,
+                        const VerificationToken& verificationToken, update_cb _hidl_cb) override {
+        return km4_0_dev_->update(operationHandle, inParams, input, authToken, verificationToken,
+                                  _hidl_cb);
+    }
+
+    Return<void> finish(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+                        const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,
+                        const HardwareAuthToken& authToken,
+                        const VerificationToken& verificationToken, finish_cb _hidl_cb) override {
+        return km4_0_dev_->finish(operationHandle, inParams, input, signature, authToken,
+                                  verificationToken, _hidl_cb);
+    }
+
+    Return<V4_0::ErrorCode> abort(uint64_t operationHandle) override {
+        return km4_0_dev_->abort(operationHandle);
+    }
+
+    /**********************************
+     * V4_1::IKeymasterDevice methods *
+     *********************************/
+
+    Return<ErrorCode> deviceLocked(bool passwordOnly,
+                                   const VerificationToken& verificationToken) override {
+        if (km4_1_dev_) return km4_1_dev_->deviceLocked(passwordOnly, verificationToken);
+        return ErrorCode::UNIMPLEMENTED;
+    }
+
+    Return<ErrorCode> earlyBootEnded() override {
+        if (km4_1_dev_) return km4_1_dev_->earlyBootEnded();
+        return ErrorCode::UNIMPLEMENTED;
+    }
+
+    Return<void> beginOp(KeyPurpose purpose, const hidl_vec<uint8_t>& keyBlob,
+                         const hidl_vec<KeyParameter>& inParams, const HardwareAuthToken& authToken,
+                         beginOp_cb _hidl_cb) override {
+        if (km4_1_dev_) return km4_1_dev_->beginOp(purpose, keyBlob, inParams, authToken, _hidl_cb);
+
+        return km4_0_dev_->begin(
+                purpose, keyBlob, inParams, authToken,
+                [&_hidl_cb](V4_0::ErrorCode errorCode, const hidl_vec<KeyParameter>& outParams,
+                            OperationHandle operationHandle) {
+                    _hidl_cb(static_cast<ErrorCode>(errorCode), outParams,
+                             new Operation(operationHandle));
+                });
+    }
+
+  private:
+    void getVersionIfNeeded();
+
+    bool haveVersion_;
+    VersionResult version_;
+    sp<V4_0::IKeymasterDevice> km4_0_dev_;
+    sp<V4_1::IKeymasterDevice> km4_1_dev_;
+};  // namespace android::hardware::keymaster::V4_1::support
+
+}  // namespace android::hardware::keymaster::V4_1::support
diff --git a/keymaster/4.1/support/include/keymasterV4_1/Operation.h b/keymaster/4.1/support/include/keymasterV4_1/Operation.h
new file mode 100644
index 0000000..902d49a
--- /dev/null
+++ b/keymaster/4.1/support/include/keymasterV4_1/Operation.h
@@ -0,0 +1,38 @@
+/*
+ ** Copyright 2020, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/keymaster/4.1/IOperation.h>
+
+#include <keymasterV4_1/keymaster_tags.h>
+
+namespace android::hardware::keymaster::V4_1::support {
+
+class Operation : public IOperation {
+  public:
+    Operation(OperationHandle handle) : handle_(handle) {}
+
+    Return<void> getOperationChallenge(getOperationChallenge_cb _hidl_cb) override {
+        _hidl_cb(V4_1::ErrorCode::OK, handle_);
+        return Void();
+    }
+
+  private:
+    OperationHandle handle_;
+};
+
+}  // namespace android::hardware::keymaster::V4_1::support
diff --git a/keymaster/4.1/support/include/keymasterV4_1/attestation_record.h b/keymaster/4.1/support/include/keymasterV4_1/attestation_record.h
new file mode 100644
index 0000000..b543bdd
--- /dev/null
+++ b/keymaster/4.1/support/include/keymasterV4_1/attestation_record.h
@@ -0,0 +1,57 @@
+/*
+ * 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/keymaster/4.1/IKeymasterDevice.h>
+#include <keymasterV4_0/attestation_record.h>
+#include <keymasterV4_0/openssl_utils.h>
+#include <keymasterV4_1/authorization_set.h>
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V4_1 {
+
+using V4_0::kAttestionRecordOid;
+using V4_0::keymaster_verified_boot_t;
+
+struct RootOfTrust {
+    SecurityLevel security_level;
+    hidl_vec<uint8_t> verified_boot_key;
+    hidl_vec<uint8_t> verified_boot_hash;
+    keymaster_verified_boot_t verified_boot_state;
+    bool device_locked;
+};
+
+struct AttestationRecord {
+    RootOfTrust root_of_trust;
+    uint32_t attestation_version;
+    SecurityLevel attestation_security_level;
+    uint32_t keymaster_version;
+    SecurityLevel keymaster_security_level;
+    hidl_vec<uint8_t> attestation_challenge;
+    AuthorizationSet software_enforced;
+    AuthorizationSet hardware_enforced;
+    hidl_vec<uint8_t> unique_id;
+};
+
+std::tuple<ErrorCode, AttestationRecord> parse_attestation_record(const hidl_vec<uint8_t>& cert);
+
+}  // namespace V4_1
+}  // namespace keymaster
+}  // namespace hardware
+}  // namespace android
diff --git a/keymaster/4.1/support/include/keymasterV4_1/authorization_set.h b/keymaster/4.1/support/include/keymasterV4_1/authorization_set.h
index afc0eaf..01605b7 100644
--- a/keymaster/4.1/support/include/keymasterV4_1/authorization_set.h
+++ b/keymaster/4.1/support/include/keymasterV4_1/authorization_set.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef HARDWARE_INTERFACES_KEYMASTER_V4_1_SUPPORT_INCLUDE_AUTHORIZATION_SET_H_
-#define HARDWARE_INTERFACES_KEYMASTER_V4_1_SUPPORT_INCLUDE_AUTHORIZATION_SET_H_
+#pragma once
 
 #include <keymasterV4_0/authorization_set.h>
 
@@ -28,5 +27,3 @@
 using V4_0::KeyParameter;
 
 }  // namespace android::hardware::keymaster::V4_1
-
-#endif  // HARDWARE_INTERFACES_KEYMASTER_V4_1_SUPPORT_INCLUDE_AUTHORIZATION_SET_H_
diff --git a/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h b/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h
index 6ffe8e1..6c186f6 100644
--- a/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h
+++ b/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h
@@ -23,16 +23,26 @@
 
 namespace android::hardware::keymaster::V4_1 {
 
+using V4_0::Algorithm;
 using V4_0::BlockMode;
 using V4_0::Digest;
 using V4_0::EcCurve;
-using V4_0::ErrorCode;
+using V4_0::HardwareAuthenticatorType;
 using V4_0::HardwareAuthToken;
+using V4_0::HmacSharingParameters;
+using V4_0::KeyBlobUsageRequirements;
+using V4_0::KeyCharacteristics;
+using V4_0::KeyFormat;
+using V4_0::KeyOrigin;
 using V4_0::KeyParameter;
+using V4_0::KeyPurpose;
+using V4_0::OperationHandle;
 using V4_0::PaddingMode;
+using V4_0::SecurityLevel;
 using V4_0::TagType;
 using V4_0::VerificationToken;
 
+using V4_0::NullOr;
 using V4_0::TypedTag;
 
 using V4_0::TAG_ACTIVE_DATETIME;
@@ -90,6 +100,7 @@
 
 DECLARE_KM_4_1_TYPED_TAG(EARLY_BOOT_ONLY);
 DECLARE_KM_4_1_TYPED_TAG(DEVICE_UNIQUE_ATTESTATION);
+DECLARE_KM_4_1_TYPED_TAG(STORAGE_KEY);
 
 }  // namespace android::hardware::keymaster::V4_1
 
diff --git a/keymaster/4.1/support/include/keymasterV4_1/keymaster_utils.h b/keymaster/4.1/support/include/keymasterV4_1/keymaster_utils.h
new file mode 100644
index 0000000..2e28002
--- /dev/null
+++ b/keymaster/4.1/support/include/keymasterV4_1/keymaster_utils.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <keymasterV4_0/keymaster_utils.h>
+
+namespace android::hardware::keymaster::V4_1::support {
+
+using V4_0::support::blob2hidlVec;
+using V4_0::support::hidlVec2AuthToken;
+
+}  // namespace android::hardware::keymaster::V4_1::support
diff --git a/keymaster/4.1/types.hal b/keymaster/4.1/types.hal
index bdf1731..f3bdcc6 100644
--- a/keymaster/4.1/types.hal
+++ b/keymaster/4.1/types.hal
@@ -26,17 +26,53 @@
      * IKeymasterDevice::earlyBootEnded() is called.
      */
     EARLY_BOOT_ONLY = TagType:BOOL | 305,
+
     /**
      * DEVICE_UNIQUE_ATTESTATION is an argument to IKeymasterDevice::attestKey().  It indicates that
-     * attestation using a device-unique key is requested, rather than a batch key.  Only
-     * SecurityLevel::STRONGBOX IKeymasterDevices may support device-unique attestations.
-     * SecurityLevel::TRUSTED_ENVIRONMENT IKeymasterDevices must return ErrorCode::INVALID_ARGUMENT
-     * if they receive DEVICE_UNIQUE_ATTESTATION.  SecurityLevel::STRONGBOX IKeymasterDevices need
-     * not support DEVICE_UNIQUE_ATTESTATION, and return ErrorCode::CANNOT_ATTEST_IDS if they do not
-     * support it.
+     * 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 IKeymasterDevices may support device-unique
+     * attestations.  SecurityLevel::TRUSTED_ENVIRONMENT IKeymasterDevices must return
+     * ErrorCode::INVALID_ARGUMENT if they receive DEVICE_UNIQUE_ATTESTATION.
+     * SecurityLevel::STRONGBOX IKeymasterDevices need not support DEVICE_UNIQUE_ATTESTATION, and
+     * return ErrorCode::CANNOT_ATTEST_IDS if they do not support it.
      *
      * IKeymasterDevice implementations that support device-unique attestation MUST add the
      * DEVICE_UNIQUE_ATTESTATION tag to device-unique attestations.
      */
     DEVICE_UNIQUE_ATTESTATION = TagType:BOOL | 720,
+
+    /**
+     * IDENTITY_CREDENTIAL_KEY is never used by IKeymasterDevice, is not a valid argument to key
+     * generation or any operation, is never returned by any method and is never used in a key
+     * attestation.  It is used in attestations produced by the IIdentityCredential HAL when that
+     * HAL attests to Credential Keys.  IIdentityCredential produces Keymaster-style attestations.
+     */
+    IDENTITY_CREDENTIAL_KEY = TagType:BOOL | 721,
+
+    /**
+     * To prevent keys from being compromised if an attacker acquires read access to system / kernel
+     * memory, some inline encryption hardware supports protecting storage encryption keys in hardware
+     * without software having access to or the ability to set the plaintext keys. Instead, software
+     * only sees wrapped version of these keys.
+     *
+     * STORAGE_KEY is used to denote that a key generated or imported is a key used for storage
+     * encryption. Keys of this type can either be generated or imported or secure imported using
+     * keymaster. exportKey() can be used to re-wrap storage key with a per-boot ephemeral key wrapped
+     * key once the key characteristics are enforced.
+     *
+     * Keys with this tag cannot be used for any operation within keymaster.
+     * ErrorCode::INVALID_OPERATION is returned when a key with Tag::STORAGE_KEY is provided to
+     * begin().
+     */
+    STORAGE_KEY = TagType:BOOL | 722,
+};
+
+enum ErrorCode : @4.0::ErrorCode {
+    EARLY_BOOT_ENDED = -73,
+    ATTESTATION_KEYS_NOT_PROVISIONED = -74,
+    ATTESTATION_IDS_NOT_PROVISIONED = -75,
+    INVALID_OPERATION = -76,
+    STORAGE_KEY_UNSUPPORTED = -77,
 };
diff --git a/keymaster/4.1/vts/functional/Android.bp b/keymaster/4.1/vts/functional/Android.bp
index f5a0c9c..c2d7fa3 100644
--- a/keymaster/4.1/vts/functional/Android.bp
+++ b/keymaster/4.1/vts/functional/Android.bp
@@ -19,12 +19,24 @@
     defaults: ["VtsHalTargetTestDefaults"],
     srcs: [
         "EarlyBootKeyTest.cpp",
+        "DeviceUniqueAttestationTest.cpp",
+        "Keymaster4_1HidlTest.cpp",
+        "UnlockedDeviceRequiredTest.cpp",
     ],
     static_libs: [
         "android.hardware.keymaster@4.0",
         "android.hardware.keymaster@4.1",
-        "libkeymaster4support",
+        "libcrypto_static",
         "libkeymaster4_1support",
+        "libkeymaster4support",
+        "libkeymaster4vtstest",
     ],
-    test_suites: ["vts-core"],
+    cflags: [
+        "-Wall",
+        "-O0",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts-core",
+    ],
 }
diff --git a/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp b/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp
new file mode 100644
index 0000000..7ea3275
--- /dev/null
+++ b/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "Keymaster4_1HidlTest.h"
+
+#include <cutils/properties.h>
+
+#include <openssl/x509.h>
+
+#include <keymasterV4_1/attestation_record.h>
+#include <keymasterV4_1/authorization_set.h>
+
+namespace android::hardware::keymaster::V4_0 {
+
+bool operator==(const AuthorizationSet& a, const AuthorizationSet& b) {
+    return std::equal(a.begin(), a.end(), b.begin(), b.end());
+}
+
+}  // namespace android::hardware::keymaster::V4_0
+
+namespace android::hardware::keymaster::V4_1 {
+
+inline ::std::ostream& operator<<(::std::ostream& os, Tag tag) {
+    return os << toString(tag);
+}
+
+namespace test {
+
+using std::string;
+using std::tuple;
+
+namespace {
+
+char nibble2hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+                       '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+string bin2hex(const hidl_vec<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;
+}
+
+struct AuthorizationSetDifferences {
+    string aName;
+    string bName;
+    AuthorizationSet aWhackB;
+    AuthorizationSet bWhackA;
+};
+
+std::ostream& operator<<(std::ostream& o, const AuthorizationSetDifferences& diffs) {
+    if (!diffs.aWhackB.empty()) {
+        o << "Set " << diffs.aName << " contains the following that " << diffs.bName << " does not"
+          << diffs.aWhackB;
+        if (!diffs.bWhackA.empty()) o << std::endl;
+    }
+
+    if (!diffs.bWhackA.empty()) {
+        o << "Set " << diffs.bName << " contains the following that " << diffs.aName << " does not"
+          << diffs.bWhackA;
+    }
+    return o;
+}
+
+// Computes and returns a \ b and b \ a ('\' is the set-difference operator, a \ b means all the
+// elements that are in a but not b, i.e. take a and whack all the elements in b) to the provided
+// stream.  The sets must be sorted.
+//
+// This provides a simple and clear view of how the two sets differ, generally much
+// easier than scrutinizing printouts of the two sets.
+AuthorizationSetDifferences difference(string aName, const AuthorizationSet& a, string bName,
+                                       const AuthorizationSet& b) {
+    AuthorizationSetDifferences diffs = {std::move(aName), std::move(bName), {}, {}};
+    std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(diffs.aWhackB));
+    std::set_difference(b.begin(), b.end(), a.begin(), a.end(), std::back_inserter(diffs.bWhackA));
+    return diffs;
+}
+
+#define DIFFERENCE(a, b) difference(#a, a, #b, b)
+
+void check_root_of_trust(const RootOfTrust& root_of_trust) {
+    char vb_meta_device_state[PROPERTY_VALUE_MAX];
+    if (property_get("ro.boot.vbmeta.device_state", vb_meta_device_state, "") == 0) return;
+
+    char vb_meta_digest[PROPERTY_VALUE_MAX];
+    EXPECT_GT(property_get("ro.boot.vbmeta.digest", vb_meta_digest, ""), 0);
+    EXPECT_EQ(vb_meta_digest, bin2hex(root_of_trust.verified_boot_hash));
+
+    // Verified boot key should be all 0's if the boot state is not verified or self signed
+    HidlBuf empty_boot_key(string(32, '\0'));
+
+    char vb_meta_bootstate[PROPERTY_VALUE_MAX];
+    auto& verified_boot_key = root_of_trust.verified_boot_key;
+    auto& verified_boot_state = root_of_trust.verified_boot_state;
+    EXPECT_GT(property_get("ro.boot.verifiedbootstate", vb_meta_bootstate, ""), 0);
+    if (!strcmp(vb_meta_bootstate, "green")) {
+        EXPECT_EQ(verified_boot_state, V4_0::KM_VERIFIED_BOOT_VERIFIED);
+        EXPECT_NE(verified_boot_key, empty_boot_key);
+    } else if (!strcmp(vb_meta_bootstate, "yellow")) {
+        EXPECT_EQ(verified_boot_state, V4_0::KM_VERIFIED_BOOT_SELF_SIGNED);
+        EXPECT_NE(verified_boot_key, empty_boot_key);
+    } else if (!strcmp(vb_meta_bootstate, "orange")) {
+        EXPECT_EQ(verified_boot_state, V4_0::KM_VERIFIED_BOOT_UNVERIFIED);
+        EXPECT_EQ(verified_boot_key, empty_boot_key);
+    } else if (!strcmp(vb_meta_bootstate, "red")) {
+        EXPECT_EQ(verified_boot_state, V4_0::KM_VERIFIED_BOOT_FAILED);
+    } else {
+        EXPECT_EQ(verified_boot_state, V4_0::KM_VERIFIED_BOOT_UNVERIFIED);
+        EXPECT_EQ(verified_boot_key, empty_boot_key);
+    }
+}
+
+void check_attestation_record(AttestationRecord attestation, const HidlBuf& challenge,
+                              AuthorizationSet expected_sw_enforced,
+                              AuthorizationSet expected_hw_enforced,
+                              SecurityLevel expected_security_level) {
+    EXPECT_EQ(41U, attestation.keymaster_version);
+    EXPECT_EQ(4U, attestation.attestation_version);
+    EXPECT_EQ(expected_security_level, attestation.attestation_security_level);
+    EXPECT_EQ(expected_security_level, attestation.keymaster_security_level);
+    EXPECT_EQ(challenge, attestation.attestation_challenge);
+
+    check_root_of_trust(attestation.root_of_trust);
+
+    // Sort all of the authorization lists, so that equality matching works.
+    expected_sw_enforced.Sort();
+    expected_hw_enforced.Sort();
+    attestation.software_enforced.Sort();
+    attestation.hardware_enforced.Sort();
+
+    EXPECT_EQ(expected_sw_enforced, attestation.software_enforced)
+            << DIFFERENCE(expected_sw_enforced, attestation.software_enforced);
+    EXPECT_EQ(expected_hw_enforced, attestation.hardware_enforced)
+            << DIFFERENCE(expected_hw_enforced, attestation.hardware_enforced);
+}
+
+}  // namespace
+
+using std::string;
+using DeviceUniqueAttestationTest = Keymaster4_1HidlTest;
+
+TEST_P(DeviceUniqueAttestationTest, StrongBoxOnly) {
+    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))));
+
+    hidl_vec<hidl_vec<uint8_t>> cert_chain;
+    EXPECT_EQ(ErrorCode::UNIMPLEMENTED,
+              convert(AttestKey(
+                      AuthorizationSetBuilder()
+                              .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+                              .Authorization(TAG_ATTESTATION_CHALLENGE, HidlBuf("challenge"))
+                              .Authorization(TAG_ATTESTATION_APPLICATION_ID, HidlBuf("foo")),
+                      &cert_chain)));
+    CheckedDeleteKey();
+
+    ASSERT_EQ(ErrorCode::OK, convert(GenerateKey(AuthorizationSetBuilder()
+                                                         .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                         .EcdsaSigningKey(EcCurve::P_256)
+                                                         .Digest(Digest::SHA_2_256)
+                                                         .Authorization(TAG_INCLUDE_UNIQUE_ID))));
+
+    EXPECT_EQ(ErrorCode::UNIMPLEMENTED,
+              convert(AttestKey(
+                      AuthorizationSetBuilder()
+                              .Authorization(TAG_ATTESTATION_CHALLENGE, HidlBuf("challenge"))
+                              .Authorization(TAG_ATTESTATION_APPLICATION_ID, HidlBuf("foo")),
+                      &cert_chain)));
+}
+
+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_CREATION_DATETIME, 1))));
+
+    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)));
+
+    EXPECT_EQ(1U, cert_chain.size());
+    auto [err, attestation] = parse_attestation_record(cert_chain[0]);
+    EXPECT_EQ(ErrorCode::OK, err);
+
+    check_attestation_record(attestation, challenge,
+                             /* sw_enforced */
+                             AuthorizationSetBuilder()
+                                     .Authorization(TAG_CREATION_DATETIME, 1)
+                                     .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_CREATION_DATETIME, 1))));
+
+    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)));
+
+    EXPECT_EQ(1U, cert_chain.size());
+    auto [err, attestation] = parse_attestation_record(cert_chain[0]);
+    EXPECT_EQ(ErrorCode::OK, err);
+
+    check_attestation_record(attestation, challenge,
+                             /* sw_enforced */
+                             AuthorizationSetBuilder()
+                                     .Authorization(TAG_CREATION_DATETIME, 1)
+                                     .Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
+                             /* hw_enforced */
+                             AuthorizationSetBuilder()
+                                     .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                     .EcdsaSigningKey(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()),
+                             SecLevel());
+}
+
+INSTANTIATE_KEYMASTER_4_1_HIDL_TEST(DeviceUniqueAttestationTest);
+
+}  // namespace test
+}  // namespace android::hardware::keymaster::V4_1
diff --git a/keymaster/4.1/vts/functional/EarlyBootKeyTest.cpp b/keymaster/4.1/vts/functional/EarlyBootKeyTest.cpp
index 4a19010..a26c688 100644
--- a/keymaster/4.1/vts/functional/EarlyBootKeyTest.cpp
+++ b/keymaster/4.1/vts/functional/EarlyBootKeyTest.cpp
@@ -14,8 +14,78 @@
  * limitations under the License.
  */
 
+#include "Keymaster4_1HidlTest.h"
+
+#include <keymasterV4_1/authorization_set.h>
+
 namespace android::hardware::keymaster::V4_1::test {
 
-// TODO(swillden): Put tests here.
+using std::string;
+
+using EarlyBootKeyTest = Keymaster4_1HidlTest;
+
+// Because VTS tests are run on fully-booted machines, we can only run negative tests for early boot
+// keys, which cannot be created or used after /data is mounted.  This is the only test we can run
+// in the normal case.  The positive test will have to be done by the Android system, when it
+// creates/uses early boot keys during boot.  It should fail to boot if the early boot key usage
+// fails.
+TEST_P(EarlyBootKeyTest, CannotCreateEarlyBootKeys) {
+    auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
+            CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::EARLY_BOOT_ENDED);
+
+    CheckedDeleteKeyData(&aesKeyData);
+    CheckedDeleteKeyData(&hmacKeyData);
+    CheckedDeleteKeyData(&rsaKeyData);
+    CheckedDeleteKeyData(&ecdsaKeyData);
+}
+
+// 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) {
+    // Should be able to create keys, since early boot has not ended
+    auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
+            CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK);
+
+    // TAG_EARLY_BOOT_ONLY should be in hw-enforced.
+    EXPECT_TRUE(contains(aesKeyData.characteristics.hardwareEnforced, TAG_EARLY_BOOT_ONLY));
+    EXPECT_TRUE(contains(hmacKeyData.characteristics.hardwareEnforced, TAG_EARLY_BOOT_ONLY));
+    EXPECT_TRUE(contains(rsaKeyData.characteristics.hardwareEnforced, TAG_EARLY_BOOT_ONLY));
+    EXPECT_TRUE(contains(ecdsaKeyData.characteristics.hardwareEnforced, 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
+    Return<ErrorCode> earlyBootResult = keymaster().earlyBootEnded();
+    EXPECT_TRUE(earlyBootResult.isOk());
+    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));
+
+    CheckedDeleteKeyData(&aesKeyData);
+    CheckedDeleteKeyData(&hmacKeyData);
+    CheckedDeleteKeyData(&rsaKeyData);
+    CheckedDeleteKeyData(&ecdsaKeyData);
+
+    // Should not be able to create new keys
+    std::tie(aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData) =
+            CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::EARLY_BOOT_ENDED);
+
+    CheckedDeleteKeyData(&aesKeyData);
+    CheckedDeleteKeyData(&hmacKeyData);
+    CheckedDeleteKeyData(&rsaKeyData);
+    CheckedDeleteKeyData(&ecdsaKeyData);
+}
+
+INSTANTIATE_KEYMASTER_4_1_HIDL_TEST(EarlyBootKeyTest);
 
 }  // namespace android::hardware::keymaster::V4_1::test
diff --git a/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.cpp b/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.cpp
new file mode 100644
index 0000000..efedf28
--- /dev/null
+++ b/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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 "Keymaster4_1HidlTest.h"
+
+namespace android::hardware::keymaster::V4_1::test {
+
+using std::string;
+
+void Keymaster4_1HidlTest::SetUp() {
+    keymaster41_ = IKeymasterDevice::getService(GetParam());
+    InitializeKeymaster(keymaster41_);
+}
+
+auto Keymaster4_1HidlTest::ProcessMessage(const HidlBuf& key_blob, KeyPurpose operation,
+                                          const string& message, const AuthorizationSet& in_params)
+        -> std::tuple<ErrorCode, string, AuthorizationSet /* out_params */> {
+    AuthorizationSet begin_out_params;
+    V4_0::ErrorCode result = Begin(operation, key_blob, in_params, &begin_out_params, &op_handle_);
+    AuthorizationSet out_params(std::move(begin_out_params));
+    if (result != V4_0::ErrorCode::OK) {
+        return {convert(result), {}, out_params};
+    }
+
+    string output;
+    size_t consumed = 0;
+    AuthorizationSet update_params;
+    AuthorizationSet update_out_params;
+    result = Update(op_handle_, update_params, message, &update_out_params, &output, &consumed);
+    out_params.push_back(update_out_params);
+    if (result != V4_0::ErrorCode::OK) {
+        return {convert(result), output, out_params};
+    }
+
+    string unused;
+    AuthorizationSet finish_params;
+    AuthorizationSet finish_out_params;
+    result = Finish(op_handle_, finish_params, message.substr(consumed), unused, &finish_out_params,
+                    &output);
+    op_handle_ = V4_0::test::kOpHandleSentinel;
+    out_params.push_back(finish_out_params);
+
+    return {convert(result), output, out_params};
+}
+
+}  // namespace android::hardware::keymaster::V4_1::test
diff --git a/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.h b/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.h
new file mode 100644
index 0000000..6332c43
--- /dev/null
+++ b/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.h
@@ -0,0 +1,158 @@
+/*
+ * 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 <android/hardware/keymaster/4.1/IKeymasterDevice.h>
+
+#include <KeymasterHidlTest.h>
+#include <keymasterV4_1/authorization_set.h>
+
+namespace android::hardware::keymaster::V4_1::test {
+
+using V4_0::test::HidlBuf;
+
+class Keymaster4_1HidlTest : public V4_0::test::KeymasterHidlTest {
+  public:
+    using super = V4_0::test::KeymasterHidlTest;
+
+    ErrorCode convert(V4_0::ErrorCode error_code) { return static_cast<ErrorCode>(error_code); }
+
+    // These methods hide the base class versions.
+    void SetUp();
+    IKeymasterDevice& keymaster() { return *keymaster41_; };
+
+    struct KeyData {
+        HidlBuf blob;
+        KeyCharacteristics characteristics;
+    };
+
+    std::tuple<ErrorCode, KeyData> GenerateKeyData(const AuthorizationSet& keyDescription) {
+        KeyData keyData;
+        ErrorCode errorCode = convert(
+                super::GenerateKey(keyDescription, &keyData.blob, &keyData.characteristics));
+        return {errorCode, keyData};
+    }
+
+    void CheckedDeleteKeyData(KeyData* keyData) { CheckedDeleteKey(&keyData->blob); }
+
+    template <typename TagType>
+    std::tuple<KeyData /* aesKey */, KeyData /* hmacKey */, KeyData /* rsaKey */,
+               KeyData /* ecdsaKey */>
+    CreateTestKeys(TagType tagToTest, ErrorCode expectedReturn) {
+        ErrorCode errorCode;
+
+        /* AES */
+        KeyData aesKeyData;
+        std::tie(errorCode, aesKeyData) =
+                GenerateKeyData(AuthorizationSetBuilder()
+                                        .AesEncryptionKey(128)
+                                        .Authorization(tagToTest)
+                                        .BlockMode(BlockMode::ECB)
+                                        .Padding(PaddingMode::NONE)
+                                        .Authorization(TAG_NO_AUTH_REQUIRED));
+        EXPECT_EQ(expectedReturn, errorCode);
+
+        /* HMAC */
+        KeyData hmacKeyData;
+        std::tie(errorCode, hmacKeyData) =
+                GenerateKeyData(AuthorizationSetBuilder()
+                                        .HmacKey(128)
+                                        .Authorization(tagToTest)
+                                        .Digest(Digest::SHA_2_256)
+                                        .Authorization(TAG_MIN_MAC_LENGTH, 128)
+                                        .Authorization(TAG_NO_AUTH_REQUIRED));
+        EXPECT_EQ(expectedReturn, errorCode);
+
+        /* RSA */
+        KeyData rsaKeyData;
+        std::tie(errorCode, rsaKeyData) =
+                GenerateKeyData(AuthorizationSetBuilder()
+                                        .RsaSigningKey(2048, 65537)
+                                        .Authorization(tagToTest)
+                                        .Digest(Digest::NONE)
+                                        .Padding(PaddingMode::NONE)
+                                        .Authorization(TAG_NO_AUTH_REQUIRED));
+        EXPECT_EQ(expectedReturn, errorCode);
+
+        /* ECDSA */
+        KeyData ecdsaKeyData;
+        std::tie(errorCode, ecdsaKeyData) =
+                GenerateKeyData(AuthorizationSetBuilder()
+                                        .EcdsaSigningKey(256)
+                                        .Authorization(tagToTest)
+                                        .Digest(Digest::SHA_2_256)
+                                        .Authorization(TAG_NO_AUTH_REQUIRED));
+        EXPECT_EQ(expectedReturn, errorCode);
+
+        return {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData};
+    }
+
+    std::tuple<ErrorCode, std::string /* processedMessage */, AuthorizationSet /* out_params */>
+    ProcessMessage(const HidlBuf& key_blob, KeyPurpose operation, const std::string& message,
+                   const AuthorizationSet& in_params);
+
+    ErrorCode UseAesKey(const HidlBuf& aesKeyBlob) {
+        auto [result, ciphertext, out_params] = ProcessMessage(
+                aesKeyBlob, KeyPurpose::ENCRYPT, "1234567890123456",
+                AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE));
+        return result;
+    }
+
+    ErrorCode UseHmacKey(const HidlBuf& hmacKeyBlob) {
+        auto [result, mac, out_params] =
+                ProcessMessage(hmacKeyBlob, KeyPurpose::SIGN, "1234567890123456",
+                               AuthorizationSetBuilder().Authorization(TAG_MAC_LENGTH, 128));
+        return result;
+    }
+
+    ErrorCode UseRsaKey(const HidlBuf& rsaKeyBlob) {
+        std::string message(2048 / 8, 'a');
+        auto [result, signature, out_params] = ProcessMessage(
+                rsaKeyBlob, KeyPurpose::SIGN, message,
+                AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+        return result;
+    }
+
+    ErrorCode UseEcdsaKey(const HidlBuf& ecdsaKeyBlob) {
+        auto [result, signature, out_params] =
+                ProcessMessage(ecdsaKeyBlob, KeyPurpose::SIGN, "a",
+                               AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
+        return result;
+    }
+
+    static std::vector<std::string> build_params() {
+        auto params = android::hardware::getAllHalInstanceNames(IKeymasterDevice::descriptor);
+        return params;
+    }
+
+  private:
+    sp<IKeymasterDevice> keymaster41_;
+};
+
+template <typename TypedTag>
+bool contains(hidl_vec<KeyParameter>& set, TypedTag typedTag) {
+    return std::find_if(set.begin(), set.end(), [&](const KeyParameter& param) {
+               return param.tag == static_cast<V4_0::Tag>(typedTag);
+           }) != set.end();
+}
+
+#define INSTANTIATE_KEYMASTER_4_1_HIDL_TEST(name)                                     \
+    INSTANTIATE_TEST_SUITE_P(PerInstance, name,                                       \
+                             testing::ValuesIn(Keymaster4_1HidlTest::build_params()), \
+                             android::hardware::PrintInstanceNameToString)
+
+}  // namespace android::hardware::keymaster::V4_1::test
diff --git a/keymaster/4.1/vts/functional/UnlockedDeviceRequiredTest.cpp b/keymaster/4.1/vts/functional/UnlockedDeviceRequiredTest.cpp
new file mode 100644
index 0000000..671bbbf
--- /dev/null
+++ b/keymaster/4.1/vts/functional/UnlockedDeviceRequiredTest.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 "Keymaster4_1HidlTest.h"
+
+#include <keymasterV4_1/authorization_set.h>
+
+namespace android::hardware::keymaster::V4_1::test {
+
+using UnlockedDeviceRequiredTest = Keymaster4_1HidlTest;
+
+// 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.
+//
+// TODO(swillden): Use the Gatekeeper HAL to enroll some test credentials which we can verify to get
+// an unlock auth token.  If that works, enable the improved test.
+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));
+
+    Return<ErrorCode> rc =
+            keymaster().deviceLocked(false /* passwordOnly */, {} /* verificationToken */);
+    ASSERT_TRUE(rc.isOk());
+    ASSERT_EQ(ErrorCode::OK, static_cast<ErrorCode>(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));
+
+    CheckedDeleteKeyData(&aesKeyData);
+    CheckedDeleteKeyData(&hmacKeyData);
+    CheckedDeleteKeyData(&rsaKeyData);
+    CheckedDeleteKeyData(&ecdsaKeyData);
+}
+
+INSTANTIATE_KEYMASTER_4_1_HIDL_TEST(UnlockedDeviceRequiredTest);
+
+}  // namespace android::hardware::keymaster::V4_1::test
diff --git a/light/aidl/Android.bp b/light/aidl/Android.bp
new file mode 100644
index 0000000..916a857
--- /dev/null
+++ b/light/aidl/Android.bp
@@ -0,0 +1,18 @@
+aidl_interface {
+    name: "android.hardware.light",
+    vendor_available: true,
+    srcs: [
+        "android/hardware/light/*.aidl",
+    ],
+    stability: "vintf",
+    backend: {
+        java: {
+            platform_apis: true,
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+        },
+    },
+}
diff --git a/light/aidl/android/hardware/light/BrightnessMode.aidl b/light/aidl/android/hardware/light/BrightnessMode.aidl
new file mode 100644
index 0000000..bc29699
--- /dev/null
+++ b/light/aidl/android/hardware/light/BrightnessMode.aidl
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.light;
+
+@VintfStability
+enum BrightnessMode {
+    /**
+     * Light brightness is managed by a user setting.
+     */
+    USER = 0,
+
+    /**
+     * Light brightness is managed by a light sensor. This is typically used
+     * to control the display backlight, but not limited to it. HALs and
+     * hardware implementations are free to support sensor for other lights or
+     * none whatsoever.
+     */
+    SENSOR = 1,
+
+    /**
+     * Use a low-persistence mode for display backlights, where the pixel
+     * color transition times are lowered.
+     *
+     * When set, the device driver must switch to a mode optimized for low display
+     * persistence that is intended to be used when the device is being treated as a
+     * head mounted display (HMD). The actual display brightness in this mode is
+     * implementation dependent, and any value set for color in LightState may be
+     * overridden by the HAL implementation.
+     *
+     * For an optimal HMD viewing experience, the display must meet the following
+     * criteria in this mode:
+     * - Gray-to-Gray, White-to-Black, and Black-to-White switching time must be ≤ 3 ms.
+     * - The display must support low-persistence with ≤ 3.5 ms persistence.
+     *   Persistence is defined as the amount of time for which a pixel is
+     *   emitting light for a single frame.
+     * - Any "smart panel" or other frame buffering options that increase display
+     *   latency are disabled.
+     * - Display brightness is set so that the display is still visible to the user
+     *   under normal indoor lighting.
+     * - The display must update at 60 Hz at least, but higher refresh rates are
+     *   recommended for low latency.
+     *
+     */
+    LOW_PERSISTENCE = 2,
+}
diff --git a/light/aidl/android/hardware/light/FlashMode.aidl b/light/aidl/android/hardware/light/FlashMode.aidl
new file mode 100644
index 0000000..00c6b6a
--- /dev/null
+++ b/light/aidl/android/hardware/light/FlashMode.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.light;
+
+@VintfStability
+enum FlashMode {
+    /**
+     * Keep the light steady on or off.
+     */
+    NONE = 0,
+    /**
+     * Flash the light at specified rate, potentially using a software-based
+     * implementation.
+     */
+    TIMED = 1,
+    /**
+     * Flash the light using hardware flashing support. This may or may not
+     * support a user-defined flashing rate or other features.
+     */
+    HARDWARE = 2,
+}
diff --git a/light/aidl/android/hardware/light/HwLight.aidl b/light/aidl/android/hardware/light/HwLight.aidl
new file mode 100644
index 0000000..43fdb4b
--- /dev/null
+++ b/light/aidl/android/hardware/light/HwLight.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.light;
+
+import android.hardware.light.LightType;
+
+/**
+ * A description of a single light. Multiple lights can map to the same physical
+ * LED. Separate physical LEDs are always represented by separate instances.
+ */
+@VintfStability
+parcelable HwLight {
+    /**
+     * Integer ID used for controlling this light
+     */
+    int id;
+
+    /**
+     * For a group of lights of the same logical type, sorting by ordinal should
+     * be give their physical order. No other meaning is carried by it.
+     */
+    int ordinal;
+
+    /**
+     * Logical type use of this light.
+     */
+    LightType type;
+}
diff --git a/light/aidl/android/hardware/light/HwLightState.aidl b/light/aidl/android/hardware/light/HwLightState.aidl
new file mode 100644
index 0000000..24d3250
--- /dev/null
+++ b/light/aidl/android/hardware/light/HwLightState.aidl
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.light;
+
+import android.hardware.light.BrightnessMode;
+import android.hardware.light.FlashMode;
+
+/**
+ * The parameters that can be set for a given light.
+ *
+ * Not all lights must support all parameters. If you
+ * can do something backward-compatible, do it.
+ */
+@VintfStability
+parcelable HwLightState {
+    /**
+     * The color of the LED in ARGB.
+     *
+     * The implementation of this in the HAL and hardware is a best-effort one.
+     *   - If a light can only do red or green and blue is requested, green
+     *     should be shown.
+     *   - If only a brightness ramp is supported, then this formula applies:
+     *      unsigned char brightness = ((77*((color>>16)&0x00ff))
+     *              + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
+     *   - If only on and off are supported, 0 is off, anything else is on.
+     *
+     * The high byte should be ignored. Callers should set it to 0xff (which
+     * would correspond to 255 alpha).
+     */
+    int color;
+
+    /**
+     * To flash the light at a given rate, set flashMode to FLASH_TIMED.
+     */
+    FlashMode flashMode;
+
+    /**
+     * flashOnMs should be set to the number of milliseconds to turn the
+     * light on, before it's turned off.
+     */
+    int flashOnMs;
+
+    /**
+     * flashOfMs should be set to the number of milliseconds to turn the
+     * light off, before it's turned back on.
+     */
+    int flashOffMs;
+
+    BrightnessMode brightnessMode;
+}
diff --git a/light/aidl/android/hardware/light/ILights.aidl b/light/aidl/android/hardware/light/ILights.aidl
new file mode 100644
index 0000000..2253f73
--- /dev/null
+++ b/light/aidl/android/hardware/light/ILights.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.light;
+
+import android.hardware.light.HwLightState;
+import android.hardware.light.HwLight;
+
+/**
+ * Allows controlling logical lights/indicators, mapped to LEDs in a
+ * hardware-specific manner by the HAL implementation.
+ */
+@VintfStability
+interface ILights {
+    /**
+     * Set light identified by id to the provided state.
+     *
+     * If control over an invalid light is requested, this method exists with
+     * EX_UNSUPPORTED_OPERATION. Control over supported lights is done on a
+     * device-specific best-effort basis and unsupported sub-features will not
+     * be reported.
+     *
+     * @param id ID of logical light to set as returned by getLights()
+     * @param state describes what the light should look like.
+     */
+    void setLightState(in int id, in HwLightState state);
+
+    /**
+     * Discover what lights are supported by the HAL implementation.
+     *
+     * @return List of available lights
+     */
+    HwLight[] getLights();
+}
diff --git a/light/aidl/android/hardware/light/LightType.aidl b/light/aidl/android/hardware/light/LightType.aidl
new file mode 100644
index 0000000..9a7f656
--- /dev/null
+++ b/light/aidl/android/hardware/light/LightType.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.light;
+
+/**
+ * These light IDs correspond to logical lights, not physical.
+ * So for example, if your INDICATOR light is in line with your
+ * BUTTONS, it might make sense to also light the INDICATOR
+ * light to a reasonable color when the BUTTONS are lit.
+ */
+@VintfStability
+enum LightType {
+    BACKLIGHT = 0,
+    KEYBOARD = 1,
+    BUTTONS = 2,
+    BATTERY = 3,
+    NOTIFICATIONS = 4,
+    ATTENTION = 5,
+    BLUETOOTH = 6,
+    WIFI = 7,
+    MICROPHONE = 8,
+}
diff --git a/light/aidl/default/Android.bp b/light/aidl/default/Android.bp
new file mode 100644
index 0000000..ae3f463
--- /dev/null
+++ b/light/aidl/default/Android.bp
@@ -0,0 +1,16 @@
+cc_binary {
+    name: "android.hardware.lights-service.example",
+    relative_install_path: "hw",
+    init_rc: ["lights-default.rc"],
+    vintf_fragments: ["lights-default.xml"],
+    vendor: true,
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "android.hardware.light-ndk_platform",
+    ],
+    srcs: [
+        "Lights.cpp",
+        "main.cpp",
+    ],
+}
diff --git a/light/aidl/default/Lights.cpp b/light/aidl/default/Lights.cpp
new file mode 100644
index 0000000..74747d5
--- /dev/null
+++ b/light/aidl/default/Lights.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 "Lights.h"
+
+#include <android-base/logging.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace light {
+
+ndk::ScopedAStatus Lights::setLightState(int id, const HwLightState& state) {
+    LOG(INFO) << "Lights setting state for id=" << id << " to color " << std::hex << state.color;
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Lights::getLights(std::vector<HwLight>* /*lights*/) {
+    LOG(INFO) << "Lights reporting supported lights";
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace light
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/light/aidl/default/Lights.h b/light/aidl/default/Lights.h
new file mode 100644
index 0000000..8cba5a1
--- /dev/null
+++ b/light/aidl/default/Lights.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/light/BnLights.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace light {
+
+// Default implementation that reports no supported lights.
+class Lights : public BnLights {
+    ndk::ScopedAStatus setLightState(int id, const HwLightState& state) override;
+    ndk::ScopedAStatus getLights(std::vector<HwLight>* types) override;
+};
+
+}  // namespace light
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/light/aidl/default/lights-default.rc b/light/aidl/default/lights-default.rc
new file mode 100644
index 0000000..687ec97
--- /dev/null
+++ b/light/aidl/default/lights-default.rc
@@ -0,0 +1,5 @@
+service vendor.light-default /vendor/bin/hw/android.hardware.lights-service.example
+    class hal
+    user nobody
+    group nobody
+    shutdown critical
diff --git a/light/aidl/default/lights-default.xml b/light/aidl/default/lights-default.xml
new file mode 100644
index 0000000..db604d6
--- /dev/null
+++ b/light/aidl/default/lights-default.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.light</name>
+        <fqname>ILights/default</fqname>
+    </hal>
+</manifest>
diff --git a/light/aidl/default/main.cpp b/light/aidl/default/main.cpp
new file mode 100644
index 0000000..a860bf4
--- /dev/null
+++ b/light/aidl/default/main.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Lights.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using ::aidl::android::hardware::light::Lights;
+
+int main() {
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    std::shared_ptr<Lights> lights = ndk::SharedRefBase::make<Lights>();
+
+    const std::string instance = std::string() + Lights::descriptor + "/default";
+    binder_status_t status = AServiceManager_addService(lights->asBinder().get(), instance.c_str());
+    CHECK(status == STATUS_OK);
+
+    ABinderProcess_joinThreadPool();
+    return EXIT_FAILURE;  // should not reached
+}
diff --git a/light/aidl/vts/functional/Android.bp b/light/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..3dd8cf6
--- /dev/null
+++ b/light/aidl/vts/functional/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "VtsHalLightTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: [
+        "VtsHalLightTargetTest.cpp",
+    ],
+    shared_libs: [
+        "libbinder",
+    ],
+    static_libs: [
+        "android.hardware.light-cpp",
+    ],
+    test_suites: [
+        "vts-core",
+    ],
+}
diff --git a/light/aidl/vts/functional/VtsHalLightTargetTest.cpp b/light/aidl/vts/functional/VtsHalLightTargetTest.cpp
new file mode 100644
index 0000000..3c26278
--- /dev/null
+++ b/light/aidl/vts/functional/VtsHalLightTargetTest.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "light_aidl_hal_test"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <android-base/logging.h>
+#include <android/hardware/light/ILights.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include <unistd.h>
+#include <set>
+
+using android::ProcessState;
+using android::sp;
+using android::String16;
+using android::binder::Status;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::light::BrightnessMode;
+using android::hardware::light::FlashMode;
+using android::hardware::light::HwLight;
+using android::hardware::light::HwLightState;
+using android::hardware::light::ILights;
+using android::hardware::light::LightType;
+
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
+
+const std::set<LightType> kAllTypes{android::enum_range<LightType>().begin(),
+                                    android::enum_range<LightType>().end()};
+
+class LightsAidl : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        lights = android::waitForDeclaredService<ILights>(String16(GetParam().c_str()));
+        ASSERT_NE(lights, nullptr);
+        ASSERT_TRUE(lights->getLights(&supportedLights).isOk());
+    }
+
+    sp<ILights> lights;
+    std::vector<HwLight> supportedLights;
+
+    virtual void TearDown() override {
+        for (const HwLight& light : supportedLights) {
+            HwLightState off;
+            off.color = 0x00000000;
+            off.flashMode = FlashMode::NONE;
+            off.brightnessMode = BrightnessMode::USER;
+            EXPECT_TRUE(lights->setLightState(light.id, off).isOk());
+        }
+
+        // must leave the device in a useable condition
+        for (const HwLight& light : supportedLights) {
+            if (light.type == LightType::BACKLIGHT) {
+                HwLightState backlightOn;
+                backlightOn.color = 0xFFFFFFFF;
+                backlightOn.flashMode = FlashMode::TIMED;
+                backlightOn.brightnessMode = BrightnessMode::USER;
+                EXPECT_TRUE(lights->setLightState(light.id, backlightOn).isOk());
+            }
+        }
+    }
+};
+
+/**
+ * Ensure all reported lights actually work.
+ */
+TEST_P(LightsAidl, TestSupported) {
+    HwLightState whiteFlashing;
+    whiteFlashing.color = 0xFFFFFFFF;
+    whiteFlashing.flashMode = FlashMode::TIMED;
+    whiteFlashing.flashOnMs = 100;
+    whiteFlashing.flashOffMs = 50;
+    whiteFlashing.brightnessMode = BrightnessMode::USER;
+    for (const HwLight& light : supportedLights) {
+        EXPECT_TRUE(lights->setLightState(light.id, whiteFlashing).isOk());
+    }
+}
+
+/**
+ * Ensure all reported lights have one of the supported types.
+ */
+TEST_P(LightsAidl, TestSupportedLightTypes) {
+    for (const HwLight& light : supportedLights) {
+        EXPECT_TRUE(kAllTypes.find(light.type) != kAllTypes.end());
+    }
+}
+
+/**
+ * Ensure all lights have a unique id.
+ */
+TEST_P(LightsAidl, TestUniqueIds) {
+    std::set<int> ids;
+    for (const HwLight& light : supportedLights) {
+        EXPECT_TRUE(ids.find(light.id) == ids.end());
+        ids.insert(light.id);
+    }
+}
+
+/**
+ * Ensure all lights have a unique ordinal for a given type.
+ */
+TEST_P(LightsAidl, TestUniqueOrdinalsForType) {
+    std::map<int, std::set<int>> ordinalsByType;
+    for (const HwLight& light : supportedLights) {
+        auto& ordinals = ordinalsByType[(int)light.type];
+        EXPECT_TRUE(ordinals.find(light.ordinal) == ordinals.end());
+        ordinals.insert(light.ordinal);
+    }
+}
+
+/**
+ * Ensure EX_UNSUPPORTED_OPERATION is returned if LOW_PERSISTENCE is not supported.
+ */
+TEST_P(LightsAidl, TestLowPersistence) {
+    HwLightState lowPersistence;
+    lowPersistence.color = 0xFF123456;
+    lowPersistence.flashMode = FlashMode::TIMED;
+    lowPersistence.flashOnMs = 100;
+    lowPersistence.flashOffMs = 50;
+    lowPersistence.brightnessMode = BrightnessMode::LOW_PERSISTENCE;
+    for (const HwLight& light : supportedLights) {
+        Status status = lights->setLightState(light.id, lowPersistence);
+        EXPECT_TRUE(status.isOk() || Status::EX_UNSUPPORTED_OPERATION == status.exceptionCode());
+    }
+}
+
+/**
+ * Ensure EX_UNSUPPORTED_OPERATION is returns for an invalid light id.
+ */
+TEST_P(LightsAidl, TestInvalidLightIdUnsupported) {
+    int maxId = INT_MIN;
+    for (const HwLight& light : supportedLights) {
+        maxId = std::max(maxId, light.id);
+    }
+
+    Status status = lights->setLightState(maxId + 1, HwLightState());
+    EXPECT_TRUE(status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION);
+}
+
+INSTANTIATE_TEST_SUITE_P(Lights, LightsAidl,
+                         testing::ValuesIn(android::getAidlHalInstanceNames(ILights::descriptor)),
+                         android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ProcessState::self()->startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/light/utils/Android.bp b/light/utils/Android.bp
index 4c287e4..e901129 100644
--- a/light/utils/Android.bp
+++ b/light/utils/Android.bp
@@ -23,7 +23,11 @@
     shared_libs: [
         "android.hardware.light@2.0",
         "libbase",
+        "libbinder",
         "libhidlbase",
         "libutils",
     ],
+    static_libs: [
+        "android.hardware.light-cpp",
+    ],
 }
diff --git a/light/utils/main.cpp b/light/utils/main.cpp
index b834132..b9b6489 100644
--- a/light/utils/main.cpp
+++ b/light/utils/main.cpp
@@ -19,34 +19,23 @@
 
 #include <android-base/logging.h>
 #include <android/hardware/light/2.0/ILight.h>
+#include <android/hardware/light/ILights.h>
+#include <binder/IServiceManager.h>
+
+using android::sp;
+using android::waitForVintfService;
+using android::binder::Status;
+using android::hardware::hidl_vec;
+
+namespace V2_0 = android::hardware::light::V2_0;
+namespace aidl = android::hardware::light;
 
 void error(const std::string& msg) {
     LOG(ERROR) << msg;
     std::cerr << msg << std::endl;
 }
 
-int main(int argc, char* argv[]) {
-    using ::android::hardware::hidl_vec;
-    using ::android::hardware::light::V2_0::Brightness;
-    using ::android::hardware::light::V2_0::Flash;
-    using ::android::hardware::light::V2_0::ILight;
-    using ::android::hardware::light::V2_0::LightState;
-    using ::android::hardware::light::V2_0::Status;
-    using ::android::hardware::light::V2_0::Type;
-    using ::android::sp;
-
-    sp<ILight> service = ILight::getService();
-    if (service == nullptr) {
-        error("Could not retrieve light service.");
-        return -1;
-    }
-
-    static LightState off = {
-            .color = 0u,
-            .flashMode = Flash::NONE,
-            .brightnessMode = Brightness::USER,
-    };
-
+int parseArgs(int argc, char* argv[], unsigned int* color) {
     if (argc > 2) {
         error("Usage: blank_screen [color]");
         return -1;
@@ -54,25 +43,78 @@
 
     if (argc > 1) {
         char* col_ptr;
-        unsigned int col_new;
 
-        col_new = strtoul(argv[1], &col_ptr, 0);
+        *color = strtoul(argv[1], &col_ptr, 0);
         if (*col_ptr != '\0') {
             error("Failed to convert " + std::string(argv[1]) + " to number");
             return -1;
         }
-        off.color = col_new;
+
+        return 0;
     }
 
-    service->getSupportedTypes([&](const hidl_vec<Type>& types) {
-        for (Type type : types) {
-            Status ret = service->setLight(type, off);
-            if (ret != Status::SUCCESS) {
-                error("Failed to shut off screen for type " +
+    *color = 0u;
+    return 0;
+}
+
+void setToColorAidl(sp<aidl::ILights> hal, unsigned int color) {
+    static aidl::HwLightState off;
+    off.color = color;
+    off.flashMode = aidl::FlashMode::NONE;
+    off.brightnessMode = aidl::BrightnessMode::USER;
+
+    std::vector<aidl::HwLight> lights;
+    Status status = hal->getLights(&lights);
+    if (!status.isOk()) {
+        error("Failed to list lights");
+        return;
+    }
+
+    for (auto light : lights) {
+        Status setStatus = hal->setLightState(light.id, off);
+        if (!setStatus.isOk()) {
+            error("Failed to shut off light id " + std::to_string(light.id));
+        }
+    }
+}
+
+void setToColorHidl(sp<V2_0::ILight> hal, unsigned int color) {
+    static V2_0::LightState off = {
+            .color = color,
+            .flashMode = V2_0::Flash::NONE,
+            .brightnessMode = V2_0::Brightness::USER,
+    };
+
+    hal->getSupportedTypes([&](const hidl_vec<V2_0::Type>& types) {
+        for (auto type : types) {
+            V2_0::Status ret = hal->setLight(type, off);
+            if (ret != V2_0::Status::SUCCESS) {
+                error("Failed to shut off light for type " +
                       std::to_string(static_cast<int>(type)));
             }
         }
     });
+}
 
-    return 0;
+int main(int argc, char* argv[]) {
+    unsigned int inputColor;
+    int result = parseArgs(argc, argv, &inputColor);
+    if (result != 0) {
+        return result;
+    }
+
+    auto aidlHal = waitForVintfService<aidl::ILights>();
+    if (aidlHal != nullptr) {
+        setToColorAidl(aidlHal, inputColor);
+        return 0;
+    }
+
+    sp<V2_0::ILight> hidlHal = V2_0::ILight::getService();
+    if (hidlHal != nullptr) {
+        setToColorHidl(hidlHal, inputColor);
+        return 0;
+    }
+
+    error("Could not retrieve light service.");
+    return -1;
 }
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
index 4909214..599fd1d 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
@@ -272,7 +272,7 @@
             int n;
             std::tie(n, outputShapes, timing, std::ignore) =
                     controller->compute(request, testConfig.measureTiming, keys);
-            executionStatus = nn::convertResultCodeToErrorStatus(n);
+            executionStatus = nn::convertToV1_0(nn::convertResultCodeToErrorStatus(n));
 
             break;
         }
diff --git a/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp b/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp
index 416744f..ec9629b 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp
@@ -296,7 +296,8 @@
     // collect serialized result by running regular burst
     const auto [nRegular, outputShapesRegular, timingRegular, fallbackRegular] =
             controllerRegular->compute(request, MeasureTiming::NO, keys);
-    const ErrorStatus statusRegular = nn::convertResultCodeToErrorStatus(nRegular);
+    const ErrorStatus statusRegular =
+            nn::convertToV1_0(nn::convertResultCodeToErrorStatus(nRegular));
     EXPECT_FALSE(fallbackRegular);
 
     // skip test if regular burst output isn't useful for testing a failure
@@ -312,7 +313,7 @@
     // large enough to return the serialized result
     const auto [nSmall, outputShapesSmall, timingSmall, fallbackSmall] =
             controllerSmall->compute(request, MeasureTiming::NO, keys);
-    const ErrorStatus statusSmall = nn::convertResultCodeToErrorStatus(nSmall);
+    const ErrorStatus statusSmall = nn::convertToV1_0(nn::convertResultCodeToErrorStatus(nSmall));
     EXPECT_NE(ErrorStatus::NONE, statusSmall);
     EXPECT_EQ(0u, outputShapesSmall.size());
     EXPECT_TRUE(badTiming(timingSmall));
diff --git a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
index 2d83b81..7b5ff9b 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
@@ -107,7 +107,7 @@
 
         // execute and verify
         const auto [n, outputShapes, timing, fallback] = burst->compute(request, measure, keys);
-        const ErrorStatus status = nn::convertResultCodeToErrorStatus(n);
+        const ErrorStatus status = nn::convertToV1_0(nn::convertResultCodeToErrorStatus(n));
         EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, status);
         EXPECT_EQ(outputShapes.size(), 0);
         EXPECT_TRUE(badTiming(timing));
diff --git a/neuralnetworks/1.3/Android.bp b/neuralnetworks/1.3/Android.bp
index 0b07a58..7b02cc5 100644
--- a/neuralnetworks/1.3/Android.bp
+++ b/neuralnetworks/1.3/Android.bp
@@ -8,7 +8,10 @@
     },
     srcs: [
         "types.hal",
+        "IBuffer.hal",
         "IDevice.hal",
+        "IExecutionCallback.hal",
+        "IFencedExecutionCallback.hal",
         "IPreparedModel.hal",
         "IPreparedModelCallback.hal",
     ],
diff --git a/neuralnetworks/1.3/IBuffer.hal b/neuralnetworks/1.3/IBuffer.hal
new file mode 100644
index 0000000..dfc57fe
--- /dev/null
+++ b/neuralnetworks/1.3/IBuffer.hal
@@ -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@1.3;
+
+import ErrorStatus;
+
+/**
+ * This interface represents a device memory buffer.
+ */
+interface IBuffer {
+    /**
+     * 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.
+     * @return status Error status of the call, must be:
+     *     - NONE if successful
+     *     - 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
+     */
+    copyTo(memory dst) generates (ErrorStatus status);
+
+    /**
+     * 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.
+     * @return status Error status of the call, must be:
+     *     - NONE if successful
+     *     - 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
+     */
+    copyFrom(memory src, vec<uint32_t> dimensions) generates (ErrorStatus status);
+};
diff --git a/neuralnetworks/1.3/IDevice.hal b/neuralnetworks/1.3/IDevice.hal
index 1295d6a..610db79 100644
--- a/neuralnetworks/1.3/IDevice.hal
+++ b/neuralnetworks/1.3/IDevice.hal
@@ -16,12 +16,20 @@
 
 package android.hardware.neuralnetworks@1.3;
 
-import @1.0::ErrorStatus;
 import @1.1::ExecutionPreference;
 import @1.2::Constant;
 import @1.2::DeviceType;
 import @1.2::Extension;
 import @1.2::IDevice;
+import BufferDesc;
+import BufferRole;
+import Capabilities;
+import ErrorStatus;
+import Model;
+import OptionalTimePoint;
+import Priority;
+import IBuffer;
+import IPreparedModel;
 import IPreparedModelCallback;
 
 /**
@@ -40,11 +48,29 @@
     getCapabilities_1_3() generates (ErrorStatus status, Capabilities capabilities);
 
     /**
+     * Returns whether the device is able to complete or abort a task within a
+     * specified duration.
+     *
+     * @return prepareModelDeadline 'true' if the device supports completing or
+     *     aborting model preparation by the deadline when the deadline is supplied,
+     *     'false' otherwise.
+     * @return executionDeadline 'true' if the device supports completing or
+     *     aborting an execution by the deadline when the deadline is supplied,
+     *     'false' otherwise.
+     */
+    supportsDeadlines() generates (bool prepareModelDeadline, bool executionDeadline);
+
+    /**
      * Gets the supported operations in a model.
      *
-     * getSupportedOperations indicates which operations of a model are fully
-     * supported by the vendor driver. If an operation may not be supported for
-     * any reason, getSupportedOperations must return false for that operation.
+     * 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.
@@ -107,6 +133,22 @@
      * 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_1_3 can be called with an optional deadline. If the model
+     * is not able to be prepared before the provided deadline, the model
+     * preparation must be aborted, and either {@link
+     * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
+     * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+     * to an abort must be sent the same way as other errors, described above.
+     * If the service reports that it does not support preparation deadlines via
+     * IDevice::supportsDeadlines, and prepareModel_1_3 is called with a
+     * deadline, then the argument is invalid, and {@link
+     * ErrorStatus::INVALID_ARGUMENT} must be returned.
+     *
      * 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
@@ -128,6 +170,11 @@
      * @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 deadline The time by which the model must be prepared. If the
+     *     model cannot be prepared by the deadline, the preparation must be
+     *     aborted.
      * @param modelCache A vector of handles with each entry holding exactly one
      *     cache file descriptor for the security-sensitive cache. The length of
      *     the vector must either be 0 indicating that caching information is
@@ -162,8 +209,12 @@
      *     - 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 deadline for preparing a model cannot be
+     *       met
+     *     - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
      */
     prepareModel_1_3(Model model, ExecutionPreference preference,
+                     Priority priority, OptionalTimePoint deadline,
                      vec<handle> modelCache, vec<handle> dataCache,
                      uint8_t[Constant:BYTE_SIZE_OF_CACHE_TOKEN] token,
                      IPreparedModelCallback callback)
@@ -209,6 +260,22 @@
      * 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.
+     *
+     * prepareModelFromCache_1_3 can be called with an optional deadline. If the
+     * model is not able to prepared before the provided deadline, the model
+     * preparation must be aborted, and either {@link
+     * ErrorStatus::MISSED_DEADLINE_TRANSIENT}
+     * or {@link ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The
+     * error due to an abort must be sent the same way as other errors,
+     * described above. If the service reports that it does not support
+     * preparation deadlines via IDevice::supportsDeadlines, and
+     * prepareModelFromCache_1_3 is called with a deadline, then the argument is
+     * invalid, and {@link ErrorStatus::INVALID_ARGUMENT} must be returned.
+     *
      * 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
@@ -217,6 +284,11 @@
      * used with different shapes of inputs on different (possibly concurrent)
      * executions.
      *
+     * @param priority The priority of the prepared model relative to other
+     *     prepared models owned by the client.
+     * @param deadline The time by which the model must be prepared. If the
+     *     model cannot be prepared by the deadline, the preparation must be
+     *     aborted.
      * @param modelCache A vector of handles with each entry holding exactly one
      *     cache file descriptor for the security-sensitive cache. The length of
      *     the vector must match the numModelCache returned from getNumberOfCacheFilesNeeded.
@@ -242,9 +314,70 @@
      *     - 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 deadline for preparing a model cannot be
+     *       met
+     *     - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
      */
-    prepareModelFromCache_1_3(vec<handle> modelCache, vec<handle> dataCache,
+    prepareModelFromCache_1_3(Priority priority, OptionalTimePoint deadline,
+                              vec<handle> modelCache, vec<handle> dataCache,
                               uint8_t[Constant:BYTE_SIZE_OF_CACHE_TOKEN] token,
                               IPreparedModelCallback callback)
             generates (ErrorStatus status);
+
+    /**
+     * 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 with an appropriate ErrorStatus, a nullptr as the IBuffer, and 0 as the
+     * buffer token. If the allocation is successful, this method must return with ErrorStatus::NONE
+     * and the produced IBuffer with 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 status Error status of the buffer allocation. Must be:
+     *     - NONE if successful
+     *     - 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
+     * @return buffer The allocated IBuffer object. If the buffer was unable to be allocated
+     *     due to an error, nullptr must be returned.
+     * @return token A positive token identifying the allocated buffer. The same token will be
+     *     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. If the buffer was unable to be allocated
+     *     due to an error, the token must be 0.
+     */
+    allocate(BufferDesc desc, vec<IPreparedModel> preparedModels, vec<BufferRole> inputRoles,
+             vec<BufferRole> outputRoles)
+            generates (ErrorStatus status, IBuffer buffer, int32_t token);
 };
diff --git a/neuralnetworks/1.3/IExecutionCallback.hal b/neuralnetworks/1.3/IExecutionCallback.hal
new file mode 100644
index 0000000..439428a
--- /dev/null
+++ b/neuralnetworks/1.3/IExecutionCallback.hal
@@ -0,0 +1,64 @@
+/*
+ * 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.neuralnetworks@1.3;
+
+import @1.2::IExecutionCallback;
+import @1.2::OutputShape;
+import @1.2::Timing;
+
+/**
+ * IExecutionCallback must be used to return the error status result from an
+ * execution asynchronously launched from IPreparedModel::execute*.
+ */
+interface IExecutionCallback extends @1.2::IExecutionCallback {
+
+    /**
+     * There are three notify methods declared for the IExecutionCallback
+     * interface: notify_1_3, notify_1_2, and notify. One of the three notify
+     * methods must be invoked immediately after the asynchronous task has
+     * finished performing the execution. One of the notify methods must be
+     * provided with the ErrorStatus from the execution. If the asynchronous
+     * task is not launched, one of the notify methods must be invoked with the
+     * appropriate error.
+     *
+     * @param status Error status returned from launching the asynchronous task
+     *               (if the launch fails) or from the asynchronous task itself
+     *               (if the launch succeeds). 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
+     *               - OUTPUT_INSUFFICIENT_SIZE if at least one output
+     *                 operand buffer is not large enough to store the
+     *                 corresponding output
+     *               - INVALID_ARGUMENT if one of the input arguments to
+     *                 prepareModel is invalid
+     *               - MISSED_DEADLINE_* if the deadline could not be met
+     *               - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
+     * @param outputShapes A list of shape information of model output operands.
+     *                     The index into "outputShapes" corresponds with to index
+     *                     of the output operand in the Request outputs vector.
+     *                     outputShapes must be empty unless the status is either
+     *                     NONE or OUTPUT_INSUFFICIENT_SIZE.
+     * @param timing Duration of execution. Unless MeasureTiming::YES was passed when
+     *               launching the execution and status is NONE, all times must
+     *               be reported as UINT64_MAX. A driver may choose to report
+     *               any time as UINT64_MAX, indicating that particular measurement is
+     *               not available.
+     */
+  oneway notify_1_3(ErrorStatus status, vec<OutputShape> outputShapes, Timing timing);
+};
diff --git a/neuralnetworks/1.3/IFencedExecutionCallback.hal b/neuralnetworks/1.3/IFencedExecutionCallback.hal
new file mode 100644
index 0000000..39076b9
--- /dev/null
+++ b/neuralnetworks/1.3/IFencedExecutionCallback.hal
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.neuralnetworks@1.3;
+
+import @1.2::Timing;
+import ErrorStatus;
+
+/**
+ * IFencedExecutionCallback can be used to query the error status result
+ * and duration information from an IPreparedModel::executeFenced call.
+ */
+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.
+     *
+     * @return status 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
+     * @return timing Duration of execution. Unless MeasureTiming::YES was passed when
+     *                launching the execution and status is NONE, all times must
+     *                be reported as UINT64_MAX. A driver may choose to report
+     *                any time as UINT64_MAX, indicating that particular measurement is
+     *                not available.
+     */
+    getExecutionInfo() generates (ErrorStatus status, Timing timing);
+};
diff --git a/neuralnetworks/1.3/IPreparedModel.hal b/neuralnetworks/1.3/IPreparedModel.hal
index 7aea416..f84bcf4 100644
--- a/neuralnetworks/1.3/IPreparedModel.hal
+++ b/neuralnetworks/1.3/IPreparedModel.hal
@@ -16,13 +16,15 @@
 
 package android.hardware.neuralnetworks@1.3;
 
-import @1.0::ErrorStatus;
-import @1.0::Request;
-import @1.2::IExecutionCallback;
 import @1.2::IPreparedModel;
 import @1.2::MeasureTiming;
 import @1.2::OutputShape;
 import @1.2::Timing;
+import ErrorStatus;
+import OptionalTimePoint;
+import Request;
+import IExecutionCallback;
+import IFencedExecutionCallback;
 
 /**
  * IPreparedModel describes a model that has been prepared for execution and
@@ -33,7 +35,8 @@
      * Launches an asynchronous execution on a prepared model.
      *
      * The execution is performed asynchronously with respect to the caller.
-     * execute_1_3 must verify the inputs to the function are correct. If there is
+     * execute_1_3 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, execute_1_3 must immediately invoke the callback with the
      * appropriate ErrorStatus value, then return with the same ErrorStatus. If
      * the inputs to the function are valid and there is no error, execute_1_3 must
@@ -64,6 +67,17 @@
      *   values, the execution should complete successfully (ErrorStatus::NONE):
      *   There must be no failure unless the device itself is in a bad state.
      *
+     * execute_1_3 can be called with an optional deadline. If the execution
+     * is not able to completed before the provided deadline, the execution
+     * must be aborted, and either {@link
+     * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
+     * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+     * to an abort must be sent the same way as other errors, described above.
+     * If the service reports that it does not support execution deadlines via
+     * IDevice::supportsDeadlines, and execute_1_3 is called with a deadline,
+     * then the argument is invalid, and {@link ErrorStatus::INVALID_ARGUMENT}
+     * must be returned.
+     *
      * Any number of calls to the execute* and executeSynchronously* functions,
      * in any combination, may be made concurrently, even on the same
      * IPreparedModel object.
@@ -74,8 +88,12 @@
      *                The duration runs from the time the driver sees the call
      *                to the execute_1_3 function to the time the driver invokes
      *                the callback.
+     * @param deadline The time by which execution must complete. If the
+     *                 execution cannot be finished by the deadline, the
+     *                 execution must be aborted.
      * @param callback A callback object used to return the error status of
-     *                 the execution. The callback object's notify function must
+     *                 the execution, shape information of model output operands, and
+     *                 duration of execution. The callback object's notify function must
      *                 be called exactly once, even if the execution was
      *                 unsuccessful.
      * @return status Error status of the call, must be:
@@ -86,8 +104,13 @@
      *                  not large enough to store the resultant values
      *                - INVALID_ARGUMENT if one of the input arguments is
      *                  invalid
+     *                - MISSED_DEADLINE_* if the deadline for executing a model
+     *                  cannot be met
+     *                - RESOURCE_EXHAUSTED_* if the task was aborted by the
+     *                  driver
      */
-    execute_1_3(Request request, MeasureTiming measure, IExecutionCallback callback)
+    execute_1_3(Request request, MeasureTiming measure, OptionalTimePoint deadline,
+                IExecutionCallback callback)
         generates (ErrorStatus status);
 
     /**
@@ -95,7 +118,8 @@
      *
      * The execution is performed synchronously with respect to the caller.
      * executeSynchronously_1_3 must verify the inputs to the function are
-     * correct. If there is an error, executeSynchronously_1_3 must immediately
+     * correct, and the usages of memory pools allocated by IDevice::allocate
+     * are valid. If there is an error, executeSynchronously_1_3 must immediately
      * return with the appropriate ErrorStatus value. If the inputs to the
      * function are valid and there is no error, executeSynchronously_1_3 must
      * perform the execution, and must not return until the execution is
@@ -114,6 +138,17 @@
      * (ErrorStatus::NONE): There must be no failure unless the device itself is
      * in a bad state.
      *
+     * executeSynchronously_1_3 can be called with an optional deadline. If the
+     * execution is not able to completed before the provided deadline, the
+     * execution must be aborted, and either {@link
+     * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
+     * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+     * to an abort must be sent the same way as other errors, described above.
+     * If the service reports that it does not support execution deadlines via
+     * IDevice::supportsDeadlines, and executeSynchronously_1_3 is called with a
+     * deadline, then the argument is invalid, and
+     * {@link ErrorStatus::INVALID_ARGUMENT} must be returned.
+     *
      * Any number of calls to the execute* and executeSynchronously* functions,
      * in any combination, may be made concurrently, even on the same
      * IPreparedModel object.
@@ -124,6 +159,9 @@
      *                The duration runs from the time the driver sees the call
      *                to the executeSynchronously_1_3 function to the time the driver
      *                returns from the function.
+     * @param deadline The time by which execution must complete. If the
+     *                 execution cannot be finished by the deadline, the
+     *                 execution must be aborted.
      * @return status Error status of the execution, must be:
      *                - NONE if execution is performed successfully
      *                - DEVICE_UNAVAILABLE if driver is offline or busy
@@ -133,16 +171,75 @@
      *                  corresponding output
      *                - INVALID_ARGUMENT if one of the input arguments is
      *                  invalid
+     *                - MISSED_DEADLINE_* if the deadline for executing a model
+     *                  cannot be met
+     *                - RESOURCE_EXHAUSTED_* if the task was aborted by the
+     *                  driver
      * @return outputShapes A list of shape information of model output operands.
      *                      The index into "outputShapes" corresponds to the index
      *                      of the output operand in the Request outputs vector.
      *                      outputShapes must be empty unless the status is either
      *                      NONE or OUTPUT_INSUFFICIENT_SIZE.
-     * @return Timing Duration of execution. Unless measure is YES and status is
+     * @return timing Duration of execution. Unless measure is YES and status is
      *                NONE, all times must be reported as UINT64_MAX. A driver may
      *                choose to report any time as UINT64_MAX, indicating that
      *                measurement is not available.
      */
-    executeSynchronously_1_3(Request request, MeasureTiming measure)
-            generates (ErrorStatus status, vec<OutputShape> outputShapes, Timing timing);
+    executeSynchronously_1_3(Request request, MeasureTiming measure,
+                             OptionalTimePoint deadline)
+                  generates (ErrorStatus status, vec<OutputShape> outputShapes,
+                             Timing timing);
+
+    /**
+     * Launch a fenced asynchronous execution on a prepared model.
+     *
+     * The execution is performed asynchronously with respect to the caller.
+     * executeFenced must fully validate the request, and only accept one that is
+     * guaranteed to be completed, unless a hardware failure or kernel panic happens on the device.
+     * If there is an error during validation, executeFenced must immediately return with
+     * the corresponding ErrorStatus. If the request is valid and there is no error launching,
+     * executeFenced must dispatch an asynchronous task to perform the execution in the
+     * background, and immediately return with ErrorStatus::NONE, a sync_fence that will be
+     * signaled once the execution is completed, and 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, empty handle may be returned for the sync fence. If the
+     * asynchronous task fails to launch, executeFenced must immediately return with
+     * ErrorStatus::GENERAL_FAILURE, and empty handle for the sync fence and nullptr
+     * for callback. The execution must wait for all the sync fences (if any) in wait_for to be
+     * signaled before starting the actual execution.
+     *
+     * If any of sync fences in wait_for changes to error status after the executeFenced
+     * call succeeds, the driver must immediately set the returned sync fence to error status.
+     *
+     * When the asynchronous task has finished its execution, it must
+     * immediately signal the sync_fence created when dispatching. After
+     * the sync_fence is signaled, the task must not modify the content of
+     * any data object referenced by 'request' (described by the
+     * {@link @1.0::DataLocation} of a {@link @1.0::RequestArgument}).
+     *
+     * Any number of calls to the executeFenced, execute* and executeSynchronously*
+     * 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 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.
+     *                The duration runs from the time the driver sees the call
+     *                to the executeFenced function to the time sync_fence is triggered.
+     * @return status Error status of the call, must be:
+     *                - NONE if task is successfully launched
+     *                - 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.
+     * @return syncFence The sync fence that will be triggered 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.
+     * @return callback The IFencedExecutionCallback can be used to query information like duration
+     *                  and error status when the execution is completed.
+     */
+    executeFenced(Request request, vec<handle> waitFor, MeasureTiming measure)
+        generates (ErrorStatus status, handle syncFence, IFencedExecutionCallback callback);
 };
diff --git a/neuralnetworks/1.3/IPreparedModelCallback.hal b/neuralnetworks/1.3/IPreparedModelCallback.hal
index ff295a2..11ebbf4 100644
--- a/neuralnetworks/1.3/IPreparedModelCallback.hal
+++ b/neuralnetworks/1.3/IPreparedModelCallback.hal
@@ -16,7 +16,6 @@
 
 package android.hardware.neuralnetworks@1.3;
 
-import @1.0::ErrorStatus;
 import @1.2::IPreparedModelCallback;
 import IPreparedModel;
 
@@ -48,6 +47,10 @@
      *                 unspecified error
      *               - INVALID_ARGUMENT if one of the input arguments to
      *                 prepareModel is invalid
+     *               - MISSED_DEADLINE_* if the deadline for executing a model
+     *                 cannot be met
+     *               - 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
diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal
index 62c5833..abc33e7 100644
--- a/neuralnetworks/1.3/types.hal
+++ b/neuralnetworks/1.3/types.hal
@@ -17,8 +17,11 @@
 package android.hardware.neuralnetworks@1.3;
 
 import @1.0::DataLocation;
-import @1.0::OperandLifeTime;
+import @1.0::ErrorStatus;
 import @1.0::PerformanceInfo;
+import @1.0::RequestArgument;
+import @1.2::Model.ExtensionNameAndPrefix;
+import @1.2::Model.ExtensionTypeEncoding;
 import @1.2::OperandType;
 import @1.2::OperationType;
 import @1.2::SymmPerChannelQuantParams;
@@ -39,6 +42,13 @@
      */
     TENSOR_QUANT8_ASYMM_SIGNED = 14,
 
+    /**
+     * A reference to a subgraph.
+     *
+     * Must have the lifetime {@link OperandLifeTime::SUBGRAPH}.
+     */
+    SUBGRAPH = 15,
+
     /*
      * DEPRECATED. Since HAL version 1.2, extensions are the preferred
      * alternative to OEM operation and data types.
@@ -67,7 +77,7 @@
 enum OperandTypeRange : uint32_t {
     BASE_MIN        = 0,
     FUNDAMENTAL_MIN = 0,
-    FUNDAMENTAL_MAX = 14,
+    FUNDAMENTAL_MAX = 15,
     OEM_MIN         = 10000,
     OEM_MAX         = 10001,
     BASE_MAX        = 0xFFFF,
@@ -1405,6 +1415,7 @@
      * * {@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
      *
@@ -1415,6 +1426,8 @@
      * * 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.
@@ -1895,6 +1908,11 @@
      * 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
@@ -1915,6 +1933,7 @@
      * 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
      *
@@ -1925,6 +1944,8 @@
      * * 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.
@@ -2176,6 +2197,7 @@
      * * {@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
      *
@@ -2186,6 +2208,8 @@
      * * 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.
@@ -2232,6 +2256,7 @@
      * 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.
      *
@@ -4876,6 +4901,192 @@
     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 as the corresponding operation inputs and outputs.
+     *
+     * Inputs:
+     * * 0: A value of type {@link OperandType::TENSOR_BOOL8} and shape [1]
+     *      that determines which of the two referenced subgraphs to execute.
+     * * 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 as the corresponding inputs of the WHILE operation
+     *      and exactly one output of {@link OperandType::TENSOR_BOOL8}
+     *      and shape [1].
+     * * 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 as the corresponding inputs and outputs of the WHILE
+     *      operation.
+     * * (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}
+     *
+     * 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}
+     *
+     * 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}
+     *
+     * 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}
+     *
+     * Inputs:
+     * * 0: The input tensor.
+     *
+     * Outputs:
+     * * 0: A scalar of {@link OperandType::INT32}, specifying the rank
+     *      of the input tensor.
+     */
+    RANK = 101,
+
+    /**
      * DEPRECATED. Since NNAPI 1.2, extensions are the preferred alternative to
      * OEM operation and data types.
      *
@@ -4897,12 +5108,21 @@
 enum OperationTypeRange : uint32_t {
     BASE_MIN        = 0,
     FUNDAMENTAL_MIN = 0,
-    FUNDAMENTAL_MAX = 95,
+    FUNDAMENTAL_MAX = 101,
     OEM_MIN         = 10000,
     OEM_MAX         = 10000,
     BASE_MAX        = 0xFFFF,
 };
 
+/**
+ * Priority given to a prepared model for execution.
+ */
+enum Priority : int32_t {
+    LOW,
+    MEDIUM,
+    HIGH,
+};
+
 
 /**
  * The capabilities of a driver.
@@ -4965,6 +5185,59 @@
 };
 
 /**
+ * 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 {
@@ -5000,7 +5273,7 @@
      *     . The operand has lifetime CONSTANT_COPY or
      *       CONSTANT_REFERENCE.
      *
-     *     . The operand has lifetime MODEL_INPUT. Fully
+     *     . The operand has lifetime SUBGRAPH_INPUT. Fully
      *       specified dimensions must either be present in the
      *       Operand or they must be provided in the corresponding
      *       RequestArgument.
@@ -5048,8 +5321,8 @@
 
     /**
      * Where to find the data for this operand.
-     * If the lifetime is TEMPORARY_VARIABLE, MODEL_INPUT, MODEL_OUTPUT, or
-     * NO_VALUE:
+     * 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.
@@ -5059,6 +5332,11 @@
      * - 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;
 
@@ -5097,32 +5375,19 @@
  */
 struct Model {
     /**
-     * All operands included in the model.
+     * The top-level subgraph.
      */
-    vec<Operand> operands;
+    Subgraph main;
 
     /**
-     * All operations included in the model.
+     * Referenced subgraphs.
      *
-     * 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 subgraph is referenced by the main subgraph or at least one other
+     * referenced subgraph.
      *
-     * Each value corresponds to the index of the operand in "operands".
+     * There must be no reference cycles.
      */
-    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;
+    vec<Subgraph> referenced;
 
     /**
      * A byte buffer containing operand data that were copied into the model.
@@ -5156,9 +5421,9 @@
      * {@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
+     * {@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
@@ -5171,37 +5436,173 @@
      * prefix corresponding to each extension name and at most one extension
      * name corresponding to each prefix.
      */
-    vec<ExtensionNameAndPrefix> extensionNameToPrefix;
+    vec<@1.2::Model.ExtensionNameAndPrefix> extensionNameToPrefix;
+};
+
+/**
+ * An excerpt of the execution graph.
+ */
+struct Subgraph {
+    /**
+     * All operands included in the subgraph.
+     */
+    vec<Operand> operands;
 
     /**
-     * A correspondence between an extension name and a prefix of operand and
-     * operation type values.
+     * 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.
      */
-    struct ExtensionNameAndPrefix {
+    vec<Operation> operations;
+
+    /**
+     * Input indexes of the subgraph. There must be at least one.
+     *
+     * Each value corresponds to the index of the operand in "operands".
+     */
+    vec<uint32_t> inputIndexes;
+
+    /**
+     * Output indexes of the subgraph. There must be at least one.
+     *
+     * Each value corresponds to the index of the operand in "operands".
+     */
+    vec<uint32_t> outputIndexes;
+};
+
+/**
+ * A buffer descriptor. Describes the properties of a buffer.
+ */
+struct BufferDesc {
+    /**
+     * Dimensions of the buffer. May have unknown dimensions or rank. A buffer with some number
+     * of unspecified dimensions is represented by setting each unspecified dimension to 0. A
+     * buffer with unspecified rank is represented by providing an empty dimensions vector.
+     */
+    vec<uint32_t> dimensions;
+};
+
+/**
+ * Describes a role of an input or output to a prepared model.
+ */
+struct BufferRole {
+    /**
+     * The index of the IPreparedModel within the "preparedModel" argument passed in
+     * IDevice::allocate.
+     */
+    uint32_t modelIndex;
+
+    /**
+     * The index of the input or output operand.
+     */
+    uint32_t ioIndex;
+
+    /**
+     * A floating-point value within the range (0.0, 1.0]. Describes how likely the
+     * buffer is to be used in the specified role. This is provided as a hint to
+     * optimize the case when multiple roles prefer different buffer locations or data
+     * layouts.
+     */
+    float frequency;
+};
+
+/**
+ * Inputs to be sent to and outputs to be retrieved from a prepared model.
+ *
+ * A Request serves two primary tasks:
+ * 1) Provides the input and output data to be used when executing the model.
+ * 2) Specifies any updates to the input operand metadata that were left
+ *    unspecified at model preparation time.
+ *
+ * An output must not overlap with any other output, with an input, or
+ * with an operand of lifetime CONSTANT_REFERENCE.
+ */
+struct Request {
+    /**
+     * Input data and information to be used in the execution of a prepared
+     * model.
+     *
+     * The index of the input corresponds to the index in Model.inputIndexes.
+     *   E.g., input[i] corresponds to Model.inputIndexes[i].
+     */
+    vec<RequestArgument> inputs;
+
+    /**
+     * Output data and information to be used in the execution of a prepared
+     * model.
+     *
+     * The index of the output corresponds to the index in Model.outputIndexes.
+     *   E.g., output[i] corresponds to Model.outputIndexes[i].
+     */
+    vec<RequestArgument> outputs;
+
+    /**
+     * A memory pool.
+     */
+    safe_union MemoryPool {
         /**
-         * The extension name.
-         *
-         * See {@link Extension::name} for the format specification.
+         * Specifies a client-managed shared memory pool.
          */
-        string name;
+        memory hidlMemory;
 
         /**
-         * The unique extension identifier within the model.
-         *
-         * See {@link Model::extensionNameToPrefix}.
+         * Specifies a driver-managed buffer. It is the token returned from IDevice::allocate,
+         * and is specific to the IDevice object.
          */
-        uint16_t prefix;
+        int32_t token;
     };
 
     /**
-     * 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.
+     * A collection of memory pools containing operand data for both the
+     * inputs and the outputs to a model.
      */
-    enum ExtensionTypeEncoding : uint8_t {
-        HIGH_BITS_PREFIX = 16,
-        LOW_BITS_TYPE = 16,
-    };
+    vec<MemoryPool> pools;
+};
+
+/**
+ * Optional time point of the steady clock (as from std::chrono::steady_clock)
+ * measured in nanoseconds.
+ */
+safe_union OptionalTimePoint {
+    /** No time point provided. */
+    Monostate none;
+
+    /**
+     * Time point of the steady clock (as from std::chrono::steady_clock)
+     * measured in nanoseconds.
+     */
+    uint64_t nanoseconds;
+};
+
+/**
+ * Return status of a function.
+ */
+enum ErrorStatus : @1.0::ErrorStatus {
+    /**
+     * 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/1.3/types.t b/neuralnetworks/1.3/types.t
index 0d20d06..a973923 100644
--- a/neuralnetworks/1.3/types.t
+++ b/neuralnetworks/1.3/types.t
@@ -19,8 +19,11 @@
 package android.hardware.neuralnetworks@1.3;
 
 import @1.0::DataLocation;
-import @1.0::OperandLifeTime;
+import @1.0::ErrorStatus;
 import @1.0::PerformanceInfo;
+import @1.0::RequestArgument;
+import @1.2::Model.ExtensionNameAndPrefix;
+import @1.2::Model.ExtensionTypeEncoding;
 import @1.2::OperandType;
 import @1.2::OperationType;
 import @1.2::SymmPerChannelQuantParams;
@@ -87,6 +90,15 @@
     BASE_MAX        = 0xFFFF,
 };
 
+/**
+ * Priority given to a prepared model for execution.
+ */
+enum Priority : int32_t {
+    LOW,
+    MEDIUM,
+    HIGH,
+};
+
 
 /**
  * The capabilities of a driver.
@@ -149,6 +161,59 @@
 };
 
 /**
+ * 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 {
@@ -184,7 +249,7 @@
      *     . The operand has lifetime CONSTANT_COPY or
      *       CONSTANT_REFERENCE.
      *
-     *     . The operand has lifetime MODEL_INPUT. Fully
+     *     . The operand has lifetime SUBGRAPH_INPUT. Fully
      *       specified dimensions must either be present in the
      *       Operand or they must be provided in the corresponding
      *       RequestArgument.
@@ -232,8 +297,8 @@
 
     /**
      * Where to find the data for this operand.
-     * If the lifetime is TEMPORARY_VARIABLE, MODEL_INPUT, MODEL_OUTPUT, or
-     * NO_VALUE:
+     * 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.
@@ -243,6 +308,11 @@
      * - 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;
 
@@ -281,32 +351,19 @@
  */
 struct Model {
     /**
-     * All operands included in the model.
+     * The top-level subgraph.
      */
-    vec<Operand> operands;
+    Subgraph main;
 
     /**
-     * All operations included in the model.
+     * Referenced subgraphs.
      *
-     * 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 subgraph is referenced by the main subgraph or at least one other
+     * referenced subgraph.
      *
-     * Each value corresponds to the index of the operand in "operands".
+     * There must be no reference cycles.
      */
-    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;
+    vec<Subgraph> referenced;
 
     /**
      * A byte buffer containing operand data that were copied into the model.
@@ -340,9 +397,9 @@
      * {@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
+     * {@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
@@ -355,37 +412,173 @@
      * prefix corresponding to each extension name and at most one extension
      * name corresponding to each prefix.
      */
-    vec<ExtensionNameAndPrefix> extensionNameToPrefix;
+    vec<@1.2::Model.ExtensionNameAndPrefix> extensionNameToPrefix;
+};
+
+/**
+ * An excerpt of the execution graph.
+ */
+struct Subgraph {
+    /**
+     * All operands included in the subgraph.
+     */
+    vec<Operand> operands;
 
     /**
-     * A correspondence between an extension name and a prefix of operand and
-     * operation type values.
+     * 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.
      */
-    struct ExtensionNameAndPrefix {
+    vec<Operation> operations;
+
+    /**
+     * Input indexes of the subgraph. There must be at least one.
+     *
+     * Each value corresponds to the index of the operand in "operands".
+     */
+    vec<uint32_t> inputIndexes;
+
+    /**
+     * Output indexes of the subgraph. There must be at least one.
+     *
+     * Each value corresponds to the index of the operand in "operands".
+     */
+    vec<uint32_t> outputIndexes;
+};
+
+/**
+ * A buffer descriptor. Describes the properties of a buffer.
+ */
+struct BufferDesc {
+    /**
+     * Dimensions of the buffer. May have unknown dimensions or rank. A buffer with some number
+     * of unspecified dimensions is represented by setting each unspecified dimension to 0. A
+     * buffer with unspecified rank is represented by providing an empty dimensions vector.
+     */
+    vec<uint32_t> dimensions;
+};
+
+/**
+ * Describes a role of an input or output to a prepared model.
+ */
+struct BufferRole {
+    /**
+     * The index of the IPreparedModel within the "preparedModel" argument passed in
+     * IDevice::allocate.
+     */
+    uint32_t modelIndex;
+
+    /**
+     * The index of the input or output operand.
+     */
+    uint32_t ioIndex;
+
+    /**
+     * A floating-point value within the range (0.0, 1.0]. Describes how likely the
+     * buffer is to be used in the specified role. This is provided as a hint to
+     * optimize the case when multiple roles prefer different buffer locations or data
+     * layouts.
+     */
+    float frequency;
+};
+
+/**
+ * Inputs to be sent to and outputs to be retrieved from a prepared model.
+ *
+ * A Request serves two primary tasks:
+ * 1) Provides the input and output data to be used when executing the model.
+ * 2) Specifies any updates to the input operand metadata that were left
+ *    unspecified at model preparation time.
+ *
+ * An output must not overlap with any other output, with an input, or
+ * with an operand of lifetime CONSTANT_REFERENCE.
+ */
+struct Request {
+    /**
+     * Input data and information to be used in the execution of a prepared
+     * model.
+     *
+     * The index of the input corresponds to the index in Model.inputIndexes.
+     *   E.g., input[i] corresponds to Model.inputIndexes[i].
+     */
+    vec<RequestArgument> inputs;
+
+    /**
+     * Output data and information to be used in the execution of a prepared
+     * model.
+     *
+     * The index of the output corresponds to the index in Model.outputIndexes.
+     *   E.g., output[i] corresponds to Model.outputIndexes[i].
+     */
+    vec<RequestArgument> outputs;
+
+    /**
+     * A memory pool.
+     */
+    safe_union MemoryPool {
         /**
-         * The extension name.
-         *
-         * See {@link Extension::name} for the format specification.
+         * Specifies a client-managed shared memory pool.
          */
-        string name;
+        memory hidlMemory;
 
         /**
-         * The unique extension identifier within the model.
-         *
-         * See {@link Model::extensionNameToPrefix}.
+         * Specifies a driver-managed buffer. It is the token returned from IDevice::allocate,
+         * and is specific to the IDevice object.
          */
-        uint16_t prefix;
+        int32_t token;
     };
 
     /**
-     * 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.
+     * A collection of memory pools containing operand data for both the
+     * inputs and the outputs to a model.
      */
-    enum ExtensionTypeEncoding : uint8_t {
-        HIGH_BITS_PREFIX = 16,
-        LOW_BITS_TYPE = 16,
-    };
+    vec<MemoryPool> pools;
+};
+
+/**
+ * Optional time point of the steady clock (as from std::chrono::steady_clock)
+ * measured in nanoseconds.
+ */
+safe_union OptionalTimePoint {
+    /** No time point provided. */
+    Monostate none;
+
+    /**
+     * Time point of the steady clock (as from std::chrono::steady_clock)
+     * measured in nanoseconds.
+     */
+    uint64_t nanoseconds;
+};
+
+/**
+ * Return status of a function.
+ */
+enum ErrorStatus : @1.0::ErrorStatus {
+    /**
+     * 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/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp
index e2795de..8e7e9b9 100644
--- a/neuralnetworks/1.3/vts/functional/Android.bp
+++ b/neuralnetworks/1.3/vts/functional/Android.bp
@@ -15,11 +15,12 @@
 //
 
 cc_library_static {
-    name: "VtsHalNeuralNetworksV1_3Callbacks",
+    name: "VtsHalNeuralNetworksV1_3_utils",
     defaults: ["VtsHalTargetTestDefaults"],
     export_include_dirs: ["include"],
     srcs: [
         "Callbacks.cpp",
+        "Utils.cpp",
     ],
     static_libs: [
         "android.hardware.neuralnetworks@1.0",
@@ -29,7 +30,7 @@
     ],
     header_libs: [
         "libbase_headers",
-    ]
+    ],
 }
 
 cc_test {
@@ -39,6 +40,7 @@
         "BasicTests.cpp",
         "CompilationCachingTests.cpp",
         "GeneratedTestHarness.cpp",
+        "QualityOfServiceTests.cpp",
         "TestAssertions.cpp",
         "ValidateBurst.cpp",
         "ValidateModel.cpp",
@@ -50,6 +52,9 @@
         "libnativewindow",
     ],
     static_libs: [
+        "VtsHalNeuralNetworksV1_0_utils",
+        "VtsHalNeuralNetworksV1_2Callbacks",
+        "VtsHalNeuralNetworksV1_3_utils",
         "android.hardware.neuralnetworks@1.0",
         "android.hardware.neuralnetworks@1.1",
         "android.hardware.neuralnetworks@1.2",
@@ -60,9 +65,7 @@
         "libhidlmemory",
         "libneuralnetworks_generated_test_harness",
         "libneuralnetworks_utils",
-        "VtsHalNeuralNetworksV1_0_utils",
-        "VtsHalNeuralNetworksV1_2Callbacks",
-        "VtsHalNeuralNetworksV1_3Callbacks",
+        "libsync",
     ],
     whole_static_libs: [
         "neuralnetworks_generated_V1_0_example",
diff --git a/neuralnetworks/1.3/vts/functional/BasicTests.cpp b/neuralnetworks/1.3/vts/functional/BasicTests.cpp
index b64dc2f..891850c 100644
--- a/neuralnetworks/1.3/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/BasicTests.cpp
@@ -21,7 +21,6 @@
 namespace android::hardware::neuralnetworks::V1_3::vts::functional {
 
 using V1_0::DeviceStatus;
-using V1_0::ErrorStatus;
 using V1_0::PerformanceInfo;
 using V1_2::Constant;
 using V1_2::DeviceType;
diff --git a/neuralnetworks/1.3/vts/functional/Callbacks.cpp b/neuralnetworks/1.3/vts/functional/Callbacks.cpp
index 4f08e72..5768e37 100644
--- a/neuralnetworks/1.3/vts/functional/Callbacks.cpp
+++ b/neuralnetworks/1.3/vts/functional/Callbacks.cpp
@@ -24,12 +24,16 @@
 
 namespace android::hardware::neuralnetworks::V1_3::implementation {
 
-using V1_0::ErrorStatus;
+using V1_2::OutputShape;
+using V1_2::Timing;
+
+constexpr Timing kNoTiming = {.timeOnDevice = std::numeric_limits<uint64_t>::max(),
+                              .timeInDriver = std::numeric_limits<uint64_t>::max()};
 
 // PreparedModelCallback methods begin here
 
-Return<void> PreparedModelCallback::notify(ErrorStatus errorStatus,
-                                           const sp<V1_0::IPreparedModel>& preparedModel) {
+Return<void> PreparedModelCallback::notifyInternal(ErrorStatus errorStatus,
+                                                   const sp<V1_0::IPreparedModel>& preparedModel) {
     {
         std::lock_guard<std::mutex> hold(mMutex);
 
@@ -48,14 +52,19 @@
     return Void();
 }
 
-Return<void> PreparedModelCallback::notify_1_2(ErrorStatus errorStatus,
-                                               const sp<V1_2::IPreparedModel>& preparedModel) {
-    return notify(errorStatus, preparedModel);
+Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus errorStatus,
+                                           const sp<V1_0::IPreparedModel>& preparedModel) {
+    return notifyInternal(static_cast<ErrorStatus>(errorStatus), preparedModel);
 }
 
-Return<void> PreparedModelCallback::notify_1_3(ErrorStatus errorStatus,
+Return<void> PreparedModelCallback::notify_1_2(V1_0::ErrorStatus errorStatus,
+                                               const sp<V1_2::IPreparedModel>& preparedModel) {
+    return notifyInternal(static_cast<ErrorStatus>(errorStatus), preparedModel);
+}
+
+Return<void> PreparedModelCallback::notify_1_3(V1_3::ErrorStatus errorStatus,
                                                const sp<V1_3::IPreparedModel>& preparedModel) {
-    return notify(errorStatus, preparedModel);
+    return notifyInternal(errorStatus, preparedModel);
 }
 
 void PreparedModelCallback::wait() const {
@@ -73,4 +82,82 @@
     return mPreparedModel;
 }
 
+// ExecutionCallback methods begin here
+
+Return<void> ExecutionCallback::notify(V1_0::ErrorStatus errorStatus) {
+    return notifyInternal(static_cast<ErrorStatus>(errorStatus), {}, kNoTiming);
+}
+
+Return<void> ExecutionCallback::notify_1_2(V1_0::ErrorStatus errorStatus,
+                                           const hidl_vec<OutputShape>& outputShapes,
+                                           const Timing& timing) {
+    return notifyInternal(static_cast<ErrorStatus>(errorStatus), outputShapes, timing);
+}
+
+Return<void> ExecutionCallback::notify_1_3(V1_3::ErrorStatus errorStatus,
+                                           const hidl_vec<OutputShape>& outputShapes,
+                                           const Timing& timing) {
+    return notifyInternal(errorStatus, outputShapes, timing);
+}
+
+void ExecutionCallback::wait() const {
+    std::unique_lock<std::mutex> lock(mMutex);
+    mCondition.wait(lock, [this] { return mNotified; });
+}
+
+ErrorStatus ExecutionCallback::getStatus() const {
+    wait();
+    return mErrorStatus;
+}
+
+const std::vector<OutputShape>& ExecutionCallback::getOutputShapes() const {
+    wait();
+    return mOutputShapes;
+}
+
+Timing ExecutionCallback::getTiming() const {
+    wait();
+    return mTiming;
+}
+
+Return<void> ExecutionCallback::notifyInternal(ErrorStatus errorStatus,
+                                               hidl_vec<OutputShape> outputShapes, Timing timing) {
+    // check results
+    if (errorStatus == ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
+        // outputShapes must not be empty if OUTPUT_INSUFFICIENT_SIZE.
+        if (outputShapes.size() == 0) {
+            LOG(ERROR) << "Notifid with empty output shape vector when OUTPUT_INSUFFICIENT_SIZE";
+            errorStatus = ErrorStatus::GENERAL_FAILURE;
+            outputShapes = {};
+            timing = kNoTiming;
+        }
+    } else if (errorStatus != ErrorStatus::NONE) {
+        // outputShapes must be empty if errorStatus is neither NONE nor OUTPUT_INSUFFICIENT_SIZE.
+        if (outputShapes.size() != 0) {
+            LOG(ERROR) << "Notified with non-empty output shape vector when error status is "
+                          "neither NONE nor OUTPUT_INSUFFICIENT_SIZE";
+            errorStatus = ErrorStatus::GENERAL_FAILURE;
+            outputShapes = {};
+            timing = kNoTiming;
+        }
+    }
+
+    // store results
+    {
+        std::lock_guard<std::mutex> hold(mMutex);
+
+        // quick-return if object has already been notified
+        if (mNotified) {
+            return Void();
+        }
+
+        mErrorStatus = errorStatus;
+        mOutputShapes = std::move(outputShapes);
+        mTiming = timing;
+        mNotified = true;
+    }
+    mCondition.notify_all();
+    return Void();
+}
+
 }  // namespace android::hardware::neuralnetworks::V1_3::implementation
diff --git a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
index 60992d5..576e524 100644
--- a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
@@ -29,6 +29,7 @@
 #include <thread>
 
 #include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
 #include "GeneratedTestHarness.h"
 #include "MemoryUtils.h"
 #include "TestHarness.h"
@@ -49,7 +50,6 @@
 
 using namespace test_helper;
 using implementation::PreparedModelCallback;
-using V1_0::ErrorStatus;
 using V1_1::ExecutionPreference;
 using V1_2::Constant;
 using V1_2::OperationType;
@@ -238,8 +238,8 @@
         mCacheDir.push_back('/');
 
         Return<void> ret = kDevice->getNumberOfCacheFilesNeeded(
-                [this](ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache) {
-                    EXPECT_EQ(ErrorStatus::NONE, status);
+                [this](V1_0::ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache) {
+                    EXPECT_EQ(V1_0::ErrorStatus::NONE, status);
                     mNumModelCache = numModelCache;
                     mNumDataCache = numDataCache;
                 });
@@ -308,7 +308,7 @@
                 model,
                 [&fullySupportsModel, &model](ErrorStatus status, const hidl_vec<bool>& supported) {
                     ASSERT_EQ(ErrorStatus::NONE, status);
-                    ASSERT_EQ(supported.size(), model.operations.size());
+                    ASSERT_EQ(supported.size(), model.main.operations.size());
                     fullySupportsModel = std::all_of(supported.begin(), supported.end(),
                                                      [](bool valid) { return valid; });
                 });
@@ -324,9 +324,9 @@
         // Launch prepare model.
         sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
         hidl_array<uint8_t, sizeof(mToken)> cacheToken(mToken);
-        Return<ErrorStatus> prepareLaunchStatus =
-                kDevice->prepareModel_1_3(model, ExecutionPreference::FAST_SINGLE_ANSWER,
-                                          modelCache, dataCache, cacheToken, preparedModelCallback);
+        Return<ErrorStatus> prepareLaunchStatus = kDevice->prepareModel_1_3(
+                model, ExecutionPreference::FAST_SINGLE_ANSWER, kDefaultPriority, {}, modelCache,
+                dataCache, cacheToken, preparedModelCallback);
         ASSERT_TRUE(prepareLaunchStatus.isOk());
         ASSERT_EQ(static_cast<ErrorStatus>(prepareLaunchStatus), ErrorStatus::NONE);
 
@@ -370,7 +370,7 @@
         sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
         hidl_array<uint8_t, sizeof(mToken)> cacheToken(mToken);
         Return<ErrorStatus> prepareLaunchStatus = kDevice->prepareModelFromCache_1_3(
-                modelCache, dataCache, cacheToken, preparedModelCallback);
+                kDefaultPriority, {}, modelCache, dataCache, cacheToken, preparedModelCallback);
         ASSERT_TRUE(prepareLaunchStatus.isOk());
         if (static_cast<ErrorStatus>(prepareLaunchStatus) != ErrorStatus::NONE) {
             *preparedModel = nullptr;
@@ -456,7 +456,7 @@
     }
 
     // Execute and verify results.
-    EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+    EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
 }
 
 TEST_P(CompilationCachingTest, CacheSavingAndRetrievalNonZeroOffset) {
@@ -518,7 +518,7 @@
     }
 
     // Execute and verify results.
-    EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+    EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
 }
 
 TEST_P(CompilationCachingTest, SaveToCacheInvalidNumCache) {
@@ -539,7 +539,7 @@
         saveModelToCache(model, modelCache, dataCache, &preparedModel);
         ASSERT_NE(preparedModel, nullptr);
         // Execute and verify results.
-        EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
         // Check if prepareModelFromCache fails.
         preparedModel = nullptr;
         ErrorStatus status;
@@ -563,7 +563,7 @@
         saveModelToCache(model, modelCache, dataCache, &preparedModel);
         ASSERT_NE(preparedModel, nullptr);
         // Execute and verify results.
-        EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
         // Check if prepareModelFromCache fails.
         preparedModel = nullptr;
         ErrorStatus status;
@@ -586,7 +586,7 @@
         saveModelToCache(model, modelCache, dataCache, &preparedModel);
         ASSERT_NE(preparedModel, nullptr);
         // Execute and verify results.
-        EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
         // Check if prepareModelFromCache fails.
         preparedModel = nullptr;
         ErrorStatus status;
@@ -610,7 +610,7 @@
         saveModelToCache(model, modelCache, dataCache, &preparedModel);
         ASSERT_NE(preparedModel, nullptr);
         // Execute and verify results.
-        EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
         // Check if prepareModelFromCache fails.
         preparedModel = nullptr;
         ErrorStatus status;
@@ -721,7 +721,7 @@
         saveModelToCache(model, modelCache, dataCache, &preparedModel);
         ASSERT_NE(preparedModel, nullptr);
         // Execute and verify results.
-        EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
         // Check if prepareModelFromCache fails.
         preparedModel = nullptr;
         ErrorStatus status;
@@ -745,7 +745,7 @@
         saveModelToCache(model, modelCache, dataCache, &preparedModel);
         ASSERT_NE(preparedModel, nullptr);
         // Execute and verify results.
-        EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
         // Check if prepareModelFromCache fails.
         preparedModel = nullptr;
         ErrorStatus status;
@@ -768,7 +768,7 @@
         saveModelToCache(model, modelCache, dataCache, &preparedModel);
         ASSERT_NE(preparedModel, nullptr);
         // Execute and verify results.
-        EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
         // Check if prepareModelFromCache fails.
         preparedModel = nullptr;
         ErrorStatus status;
@@ -792,7 +792,7 @@
         saveModelToCache(model, modelCache, dataCache, &preparedModel);
         ASSERT_NE(preparedModel, nullptr);
         // Execute and verify results.
-        EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
         // Check if prepareModelFromCache fails.
         preparedModel = nullptr;
         ErrorStatus status;
@@ -904,7 +904,7 @@
         saveModelToCache(model, modelCache, dataCache, &preparedModel);
         ASSERT_NE(preparedModel, nullptr);
         // Execute and verify results.
-        EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
         // Check if prepareModelFromCache fails.
         preparedModel = nullptr;
         ErrorStatus status;
@@ -926,7 +926,7 @@
         saveModelToCache(model, modelCache, dataCache, &preparedModel);
         ASSERT_NE(preparedModel, nullptr);
         // Execute and verify results.
-        EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
         // Check if prepareModelFromCache fails.
         preparedModel = nullptr;
         ErrorStatus status;
@@ -1070,7 +1070,8 @@
                 ASSERT_EQ(preparedModel, nullptr);
             } else {
                 ASSERT_NE(preparedModel, nullptr);
-                EvaluatePreparedModel(preparedModel, testModelAdd, /*testKind=*/TestKind::GENERAL);
+                EvaluatePreparedModel(kDevice, preparedModel, testModelAdd,
+                                      /*testKind=*/TestKind::GENERAL);
             }
         }
     }
@@ -1131,7 +1132,8 @@
                 ASSERT_EQ(preparedModel, nullptr);
             } else {
                 ASSERT_NE(preparedModel, nullptr);
-                EvaluatePreparedModel(preparedModel, testModelAdd, /*testKind=*/TestKind::GENERAL);
+                EvaluatePreparedModel(kDevice, preparedModel, testModelAdd,
+                                      /*testKind=*/TestKind::GENERAL);
             }
         }
     }
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
index eced063..88837db 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
@@ -29,11 +29,13 @@
 #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/IFencedExecutionCallback.h>
 #include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
 #include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h>
 #include <android/hardware/neuralnetworks/1.3/types.h>
 #include <android/hidl/allocator/1.0/IAllocator.h>
 #include <android/hidl/memory/1.0/IMemory.h>
+#include <android/sync.h>
 #include <gtest/gtest.h>
 #include <hidlmemory/mapping.h>
 
@@ -44,8 +46,8 @@
 #include <vector>
 
 #include "1.0/Utils.h"
-#include "1.2/Callbacks.h"
 #include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
 #include "ExecutionBurstController.h"
 #include "MemoryUtils.h"
 #include "TestHarness.h"
@@ -56,47 +58,136 @@
 
 using namespace test_helper;
 using hidl::memory::V1_0::IMemory;
+using implementation::ExecutionCallback;
 using implementation::PreparedModelCallback;
 using V1_0::DataLocation;
-using V1_0::ErrorStatus;
-using V1_0::OperandLifeTime;
-using V1_0::Request;
+using V1_0::RequestArgument;
 using V1_1::ExecutionPreference;
 using V1_2::Constant;
 using V1_2::MeasureTiming;
 using V1_2::OutputShape;
 using V1_2::SymmPerChannelQuantParams;
 using V1_2::Timing;
-using V1_2::implementation::ExecutionCallback;
 using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
 
 namespace {
 
-enum class Executor { ASYNC, SYNC, BURST };
+enum class Executor { ASYNC, SYNC, BURST, FENCED };
 
 enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT };
 
+enum class MemoryType { SHARED, DEVICE };
+
+enum class IOType { INPUT, OUTPUT };
+
 struct TestConfig {
     Executor executor;
     MeasureTiming 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, MeasureTiming measureTiming, OutputType outputType)
+    TestConfig(Executor executor, MeasureTiming measureTiming, OutputType outputType,
+               MemoryType memoryType)
         : executor(executor),
           measureTiming(measureTiming),
           outputType(outputType),
+          memoryType(memoryType),
           reportSkipping(true) {}
     TestConfig(Executor executor, MeasureTiming measureTiming, OutputType outputType,
-               bool reportSkipping)
+               MemoryType memoryType, bool reportSkipping)
         : executor(executor),
           measureTiming(measureTiming),
           outputType(outputType),
+          memoryType(memoryType),
           reportSkipping(reportSkipping) {}
 };
 
+class DeviceMemoryAllocator {
+  public:
+    DeviceMemoryAllocator(const sp<IDevice>& device, const sp<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<sp<IBuffer>, int32_t> allocate(uint32_t index) {
+        std::pair<sp<IBuffer>, int32_t> buffer;
+        allocateInternal<ioType>(index, &buffer);
+        return buffer;
+    }
+
+  private:
+    template <IOType ioType>
+    void allocateInternal(uint32_t index, std::pair<sp<IBuffer>, int32_t>* result) {
+        ASSERT_NE(result, nullptr);
+
+        // Prepare arguments.
+        BufferRole role = {.modelIndex = 0, .ioIndex = index, .frequency = 1.0f};
+        hidl_vec<BufferRole> inputRoles, outputRoles;
+        if constexpr (ioType == IOType::INPUT) {
+            inputRoles = {role};
+        } else {
+            outputRoles = {role};
+        }
+
+        // Allocate device memory.
+        ErrorStatus status;
+        sp<IBuffer> buffer;
+        int32_t token;
+        const auto ret = kDevice->allocate(
+                {}, {kPreparedModel}, inputRoles, outputRoles,
+                [&status, &buffer, &token](ErrorStatus error, const sp<IBuffer>& buf, int32_t tok) {
+                    status = error;
+                    buffer = buf;
+                    token = tok;
+                });
+
+        // Check allocation results.
+        ASSERT_TRUE(ret.isOk());
+        if (status == ErrorStatus::NONE) {
+            ASSERT_NE(buffer, nullptr);
+            ASSERT_GT(token, 0);
+        } else {
+            ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+            ASSERT_EQ(buffer, nullptr);
+            ASSERT_EQ(token, 0);
+        }
+
+        // Initialize input data from TestBuffer.
+        if constexpr (ioType == IOType::INPUT) {
+            if (buffer != nullptr) {
+                // TestBuffer -> Shared memory.
+                const auto& testBuffer = kTestModel.operands[kTestModel.inputIndexes[index]].data;
+                ASSERT_GT(testBuffer.size(), 0);
+                hidl_memory tmp = nn::allocateSharedMemory(testBuffer.size());
+                sp<IMemory> inputMemory = mapMemory(tmp);
+                ASSERT_NE(inputMemory.get(), nullptr);
+                uint8_t* inputPtr =
+                        static_cast<uint8_t*>(static_cast<void*>(inputMemory->getPointer()));
+                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->copyFrom(tmp, {});
+                ASSERT_TRUE(ret.isOk());
+                ASSERT_EQ(static_cast<ErrorStatus>(ret), ErrorStatus::NONE);
+            }
+        }
+        *result = {std::move(buffer), token};
+    }
+
+    const sp<IDevice> kDevice;
+    const sp<IPreparedModel> kPreparedModel;
+    const TestModel& kTestModel;
+};
+
 }  // namespace
 
 Model createModel(const TestModel& testModel) {
@@ -178,10 +269,10 @@
         }
     }
 
-    return {.operands = std::move(operands),
-            .operations = std::move(operations),
-            .inputIndexes = testModel.inputIndexes,
-            .outputIndexes = testModel.outputIndexes,
+    return {.main = {.operands = std::move(operands),
+                     .operations = std::move(operations),
+                     .inputIndexes = testModel.inputIndexes,
+                     .outputIndexes = testModel.outputIndexes},
             .operandValues = std::move(operandValues),
             .pools = std::move(pools),
             .relaxComputationFloat32toFloat16 = testModel.isRelaxed};
@@ -199,16 +290,171 @@
 }
 
 static void makeOutputDimensionsUnspecified(Model* model) {
-    for (auto i : model->outputIndexes) {
-        auto& dims = model->operands[i].dimensions;
+    for (auto i : model->main.outputIndexes) {
+        auto& dims = model->main.operands[i].dimensions;
         std::fill(dims.begin(), dims.end(), 0);
     }
 }
 
+constexpr uint32_t kInputPoolIndex = 0;
+constexpr uint32_t kOutputPoolIndex = 1;
+constexpr uint32_t kDeviceMemoryBeginIndex = 2;
+
+static std::pair<Request, std::vector<sp<IBuffer>>> createRequest(
+        const sp<IDevice>& device, const sp<IPreparedModel>& preparedModel,
+        const TestModel& testModel, bool preferDeviceMemory) {
+    // 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(device, preparedModel, testModel);
+    std::vector<sp<IBuffer>> buffers;
+    std::vector<int32_t> tokens;
+
+    // Model inputs.
+    hidl_vec<RequestArgument> inputs(testModel.inputIndexes.size());
+    size_t inputSize = 0;
+    for (uint32_t i = 0; i < testModel.inputIndexes.size(); i++) {
+        const auto& op = testModel.operands[testModel.inputIndexes[i]];
+        if (op.data.size() == 0) {
+            // Omitted input.
+            inputs[i] = {.hasNoValue = true};
+            continue;
+        } else if (preferDeviceMemory) {
+            SCOPED_TRACE("Input index = " + std::to_string(i));
+            auto [buffer, token] = allocator.allocate<IOType::INPUT>(i);
+            if (buffer != nullptr) {
+                DataLocation loc = {.poolIndex = static_cast<uint32_t>(buffers.size() +
+                                                                       kDeviceMemoryBeginIndex)};
+                buffers.push_back(std::move(buffer));
+                tokens.push_back(token);
+                inputs[i] = {.hasNoValue = false, .location = loc, .dimensions = {}};
+                continue;
+            }
+        }
+
+        // Reserve shared memory for input.
+        DataLocation loc = {.poolIndex = kInputPoolIndex,
+                            .offset = static_cast<uint32_t>(inputSize),
+                            .length = static_cast<uint32_t>(op.data.size())};
+        inputSize += op.data.alignedSize();
+        inputs[i] = {.hasNoValue = false, .location = loc, .dimensions = {}};
+    }
+
+    // Model outputs.
+    hidl_vec<RequestArgument> outputs(testModel.outputIndexes.size());
+    size_t outputSize = 0;
+    for (uint32_t i = 0; i < testModel.outputIndexes.size(); i++) {
+        const auto& op = testModel.operands[testModel.outputIndexes[i]];
+        if (preferDeviceMemory) {
+            SCOPED_TRACE("Output index = " + std::to_string(i));
+            auto [buffer, token] = allocator.allocate<IOType::OUTPUT>(i);
+            if (buffer != nullptr) {
+                DataLocation loc = {.poolIndex = static_cast<uint32_t>(buffers.size() +
+                                                                       kDeviceMemoryBeginIndex)};
+                buffers.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.
+        DataLocation loc = {.poolIndex = kOutputPoolIndex,
+                            .offset = static_cast<uint32_t>(outputSize),
+                            .length = static_cast<uint32_t>(bufferSize)};
+        outputSize += op.data.size() == 0 ? TestBuffer::kAlignment : op.data.alignedSize();
+        outputs[i] = {.hasNoValue = false, .location = loc, .dimensions = {}};
+    }
+
+    // Memory pools.
+    hidl_vec<Request::MemoryPool> pools(kDeviceMemoryBeginIndex + buffers.size());
+    pools[kInputPoolIndex].hidlMemory(nn::allocateSharedMemory(std::max<size_t>(inputSize, 1)));
+    pools[kOutputPoolIndex].hidlMemory(nn::allocateSharedMemory(std::max<size_t>(outputSize, 1)));
+    CHECK_NE(pools[kInputPoolIndex].hidlMemory().size(), 0u);
+    CHECK_NE(pools[kOutputPoolIndex].hidlMemory().size(), 0u);
+    for (uint32_t i = 0; i < buffers.size(); i++) {
+        pools[kDeviceMemoryBeginIndex + i].token(tokens[i]);
+    }
+
+    // Copy input data to the input shared memory pool.
+    sp<IMemory> inputMemory = mapMemory(pools[kInputPoolIndex].hidlMemory());
+    CHECK(inputMemory.get() != nullptr);
+    uint8_t* inputPtr = static_cast<uint8_t*>(static_cast<void*>(inputMemory->getPointer()));
+    CHECK(inputPtr != nullptr);
+    for (uint32_t i = 0; i < testModel.inputIndexes.size(); i++) {
+        if (!inputs[i].hasNoValue && inputs[i].location.poolIndex == kInputPoolIndex) {
+            const auto& op = testModel.operands[testModel.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);
+        }
+    }
+
+    Request request = {
+            .inputs = std::move(inputs), .outputs = std::move(outputs), .pools = std::move(pools)};
+    return {std::move(request), std::move(buffers)};
+}
+
+// Get a TestBuffer with data copied from an IBuffer object.
+static void getBuffer(const sp<IBuffer>& buffer, size_t size, TestBuffer* testBuffer) {
+    // IBuffer -> Shared memory.
+    hidl_memory tmp = nn::allocateSharedMemory(size);
+    const auto ret = buffer->copyTo(tmp);
+    ASSERT_TRUE(ret.isOk());
+    ASSERT_EQ(static_cast<ErrorStatus>(ret), ErrorStatus::NONE);
+
+    // Shared memory -> TestBuffer.
+    sp<IMemory> outputMemory = mapMemory(tmp);
+    ASSERT_NE(outputMemory.get(), nullptr);
+    uint8_t* outputPtr = static_cast<uint8_t*>(static_cast<void*>(outputMemory->getPointer()));
+    ASSERT_NE(outputPtr, nullptr);
+    ASSERT_NE(testBuffer, nullptr);
+    *testBuffer = TestBuffer(size, outputPtr);
+}
+
+static std::vector<TestBuffer> getOutputBuffers(const TestModel& testModel, const Request& request,
+                                                const std::vector<sp<IBuffer>>& buffers) {
+    sp<IMemory> outputMemory = mapMemory(request.pools[kOutputPoolIndex].hidlMemory());
+    CHECK(outputMemory.get() != nullptr);
+    uint8_t* outputPtr = static_cast<uint8_t*>(static_cast<void*>(outputMemory->getPointer()));
+    CHECK(outputPtr != nullptr);
+
+    // Copy out output results.
+    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.operands[testModel.outputIndexes[i]];
+            if (op.data.size() == 0) {
+                outputBuffers.emplace_back();
+            } else {
+                SCOPED_TRACE("Output index = " + std::to_string(i));
+                const uint32_t bufferIndex = outputLoc.poolIndex - kDeviceMemoryBeginIndex;
+                TestBuffer buffer;
+                getBuffer(buffers[bufferIndex], op.data.size(), &buffer);
+                outputBuffers.push_back(std::move(buffer));
+            }
+        }
+    }
+    return outputBuffers;
+}
+
 static Return<ErrorStatus> ExecutePreparedModel(const sp<IPreparedModel>& preparedModel,
                                                 const Request& request, MeasureTiming measure,
                                                 sp<ExecutionCallback>& callback) {
-    return preparedModel->execute_1_3(request, measure, callback);
+    return preparedModel->execute_1_3(request, measure, {}, callback);
 }
 static Return<ErrorStatus> ExecutePreparedModel(const sp<IPreparedModel>& preparedModel,
                                                 const Request& request, MeasureTiming measure,
@@ -216,7 +462,7 @@
                                                 Timing* timing) {
     ErrorStatus result;
     Return<void> ret = preparedModel->executeSynchronously_1_3(
-            request, measure,
+            request, measure, {},
             [&result, outputShapes, timing](ErrorStatus error, const hidl_vec<OutputShape>& shapes,
                                             const Timing& time) {
                 result = error;
@@ -234,8 +480,9 @@
                                                          std::chrono::microseconds{0});
 }
 
-void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
-                           const TestConfig& testConfig, bool* skipped = nullptr) {
+void EvaluatePreparedModel(const sp<IDevice>& device, const sp<IPreparedModel>& preparedModel,
+                           const TestModel& testModel, const TestConfig& testConfig,
+                           bool* skipped = nullptr) {
     if (skipped != nullptr) {
         *skipped = false;
     }
@@ -245,7 +492,13 @@
         return;
     }
 
-    Request request = createRequest(testModel);
+    auto [request, buffers] =
+            createRequest(device, preparedModel, testModel,
+                          /*preferDeviceMemory=*/testConfig.memoryType == MemoryType::DEVICE);
+    // Skip if testing memory domain but no device memory has been allocated.
+    if (testConfig.memoryType == MemoryType::DEVICE && buffers.empty()) {
+        return;
+    }
     if (testConfig.outputType == OutputType::INSUFFICIENT) {
         makeOutputInsufficientSize(/*outputIndex=*/0, &request);
     }
@@ -284,30 +537,75 @@
             break;
         }
         case Executor::BURST: {
+            // TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains
+            //                      V1_2.
             SCOPED_TRACE("burst");
 
+            // check compliance
+            ASSERT_TRUE(nn::compliantWithV1_0(request));
+            V1_0::Request request10 = nn::convertToV1_0(request);
+
             // create burst
             const std::shared_ptr<::android::nn::ExecutionBurstController> controller =
                     CreateBurst(preparedModel);
             ASSERT_NE(nullptr, controller.get());
 
             // create memory keys
-            std::vector<intptr_t> keys(request.pools.size());
+            std::vector<intptr_t> keys(request10.pools.size());
             for (size_t i = 0; i < keys.size(); ++i) {
-                keys[i] = reinterpret_cast<intptr_t>(&request.pools[i]);
+                keys[i] = reinterpret_cast<intptr_t>(&request10.pools[i]);
             }
 
             // execute burst
             int n;
             std::tie(n, outputShapes, timing, std::ignore) =
-                    controller->compute(request, testConfig.measureTiming, keys);
+                    controller->compute(request10, testConfig.measureTiming, keys);
             executionStatus = nn::convertResultCodeToErrorStatus(n);
 
             break;
         }
+        case Executor::FENCED: {
+            SCOPED_TRACE("fenced");
+            ErrorStatus result;
+            hidl_handle sync_fence_handle;
+            sp<IFencedExecutionCallback> fenced_callback;
+            Return<void> ret = preparedModel->executeFenced(
+                    request, {}, testConfig.measureTiming,
+                    [&result, &sync_fence_handle, &fenced_callback](
+                            ErrorStatus error, const hidl_handle& handle,
+                            const sp<IFencedExecutionCallback>& callback) {
+                        result = error;
+                        sync_fence_handle = handle;
+                        fenced_callback = callback;
+                    });
+            ASSERT_TRUE(ret.isOk());
+            if (result != ErrorStatus::NONE) {
+                ASSERT_EQ(sync_fence_handle.getNativeHandle(), nullptr);
+                ASSERT_EQ(fenced_callback, nullptr);
+                executionStatus = ErrorStatus::GENERAL_FAILURE;
+            } else if (sync_fence_handle.getNativeHandle()) {
+                constexpr int kInfiniteTimeout = -1;
+                int sync_fd = sync_fence_handle.getNativeHandle()->data[0];
+                ASSERT_GT(sync_fd, 0);
+                int r = sync_wait(sync_fd, kInfiniteTimeout);
+                ASSERT_GE(r, 0);
+            }
+            if (result == ErrorStatus::NONE) {
+                ASSERT_NE(fenced_callback, nullptr);
+                Return<void> ret = fenced_callback->getExecutionInfo(
+                        [&executionStatus, &timing](ErrorStatus error, Timing t) {
+                            executionStatus = error;
+                            timing = t;
+                        });
+                ASSERT_TRUE(ret.isOk());
+            }
+            break;
+        }
     }
 
-    if (testConfig.outputType != OutputType::FULLY_SPECIFIED &&
+    // The driver is allowed to reject executeFenced, and if they do, we should skip.
+    if ((testConfig.outputType != OutputType::FULLY_SPECIFIED ||
+         testConfig.executor == Executor::FENCED) &&
         executionStatus == ErrorStatus::GENERAL_FAILURE) {
         if (skipped != nullptr) {
             *skipped = true;
@@ -361,17 +659,18 @@
     }
 
     // Retrieve execution results.
-    const std::vector<TestBuffer> outputs = getOutputBuffers(request);
+    const std::vector<TestBuffer> outputs = getOutputBuffers(testModel, request, buffers);
 
     // We want "close-enough" results.
     checkResults(testModel, outputs);
 }
 
-void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
-                           TestKind testKind) {
+void EvaluatePreparedModel(const sp<IDevice>& device, const sp<IPreparedModel>& preparedModel,
+                           const TestModel& testModel, TestKind testKind) {
     std::vector<OutputType> outputTypesList;
     std::vector<MeasureTiming> measureTimingList;
     std::vector<Executor> executorList;
+    MemoryType memoryType = MemoryType::SHARED;
 
     switch (testKind) {
         case TestKind::GENERAL: {
@@ -384,6 +683,17 @@
             measureTimingList = {MeasureTiming::NO, MeasureTiming::YES};
             executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST};
         } break;
+        case TestKind::MEMORY_DOMAIN: {
+            outputTypesList = {OutputType::FULLY_SPECIFIED};
+            measureTimingList = {MeasureTiming::NO};
+            executorList = {Executor::ASYNC, Executor::SYNC};
+            memoryType = MemoryType::DEVICE;
+        } break;
+        case TestKind::FENCED_COMPUTE: {
+            outputTypesList = {OutputType::FULLY_SPECIFIED};
+            measureTimingList = {MeasureTiming::NO, MeasureTiming::YES};
+            executorList = {Executor::FENCED};
+        } break;
         case TestKind::QUANTIZATION_COUPLING: {
             LOG(FATAL) << "Wrong TestKind for EvaluatePreparedModel";
             return;
@@ -393,30 +703,32 @@
     for (const OutputType outputType : outputTypesList) {
         for (const MeasureTiming measureTiming : measureTimingList) {
             for (const Executor executor : executorList) {
-                const TestConfig testConfig(executor, measureTiming, outputType);
-                EvaluatePreparedModel(preparedModel, testModel, testConfig);
+                const TestConfig testConfig(executor, measureTiming, outputType, memoryType);
+                EvaluatePreparedModel(device, preparedModel, testModel, testConfig);
             }
         }
     }
 }
 
-void EvaluatePreparedCoupledModels(const sp<IPreparedModel>& preparedModel,
+void EvaluatePreparedCoupledModels(const sp<IDevice>& device,
+                                   const sp<IPreparedModel>& preparedModel,
                                    const TestModel& testModel,
                                    const sp<IPreparedModel>& preparedCoupledModel,
                                    const TestModel& coupledModel) {
     const std::vector<OutputType> outputTypesList = {OutputType::FULLY_SPECIFIED};
     const std::vector<MeasureTiming> measureTimingList = {MeasureTiming::NO, MeasureTiming::YES};
-    const std::vector<Executor> executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST};
+    const std::vector<Executor> executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST,
+                                                Executor::FENCED};
 
     for (const OutputType outputType : outputTypesList) {
         for (const MeasureTiming measureTiming : measureTimingList) {
             for (const Executor executor : executorList) {
-                const TestConfig testConfig(executor, measureTiming, outputType,
+                const TestConfig testConfig(executor, measureTiming, outputType, MemoryType::SHARED,
                                             /*reportSkipping=*/false);
                 bool baseSkipped = false;
-                EvaluatePreparedModel(preparedModel, testModel, testConfig, &baseSkipped);
+                EvaluatePreparedModel(device, preparedModel, testModel, testConfig, &baseSkipped);
                 bool coupledSkipped = false;
-                EvaluatePreparedModel(preparedCoupledModel, coupledModel, testConfig,
+                EvaluatePreparedModel(device, preparedCoupledModel, coupledModel, testConfig,
                                       &coupledSkipped);
                 ASSERT_EQ(baseSkipped, coupledSkipped);
                 if (baseSkipped) {
@@ -441,19 +753,18 @@
 
     sp<IPreparedModel> preparedModel;
     switch (testKind) {
-        case TestKind::GENERAL: {
+        case TestKind::GENERAL:
+        case TestKind::DYNAMIC_SHAPE:
+        case TestKind::MEMORY_DOMAIN:
+        case TestKind::FENCED_COMPUTE: {
             createPreparedModel(device, model, &preparedModel);
             if (preparedModel == nullptr) return;
-            EvaluatePreparedModel(preparedModel, testModel, TestKind::GENERAL);
-        } break;
-        case TestKind::DYNAMIC_SHAPE: {
-            createPreparedModel(device, model, &preparedModel);
-            if (preparedModel == nullptr) return;
-            EvaluatePreparedModel(preparedModel, testModel, TestKind::DYNAMIC_SHAPE);
+            EvaluatePreparedModel(device, preparedModel, testModel, testKind);
         } break;
         case TestKind::QUANTIZATION_COUPLING: {
             ASSERT_TRUE(testModel.hasQuant8CoupledOperands());
-            createPreparedModel(device, model, &preparedModel, /*reportSkipping*/ false);
+            createPreparedModel(device, model, &preparedModel,
+                                /*reportSkipping*/ false);
             TestModel signedQuantizedModel = convertQuant8AsymmOperandsToSigned(testModel);
             sp<IPreparedModel> preparedCoupledModel;
             createPreparedModel(device, createModel(signedQuantizedModel), &preparedCoupledModel,
@@ -473,7 +784,7 @@
                 GTEST_SKIP();
             }
             ASSERT_NE(preparedCoupledModel, nullptr);
-            EvaluatePreparedCoupledModels(preparedModel, testModel, preparedCoupledModel,
+            EvaluatePreparedCoupledModels(device, preparedModel, testModel, preparedCoupledModel,
                                           signedQuantizedModel);
         } break;
     }
@@ -482,6 +793,12 @@
 void GeneratedTestBase::SetUp() {
     testing::TestWithParam<GeneratedTestParam>::SetUp();
     ASSERT_NE(kDevice, nullptr);
+
+    const Return<void> ret =
+            kDevice->supportsDeadlines([this](bool prepareModelDeadline, bool executionDeadline) {
+                mSupportsDeadlines = {prepareModelDeadline, executionDeadline};
+            });
+    ASSERT_TRUE(ret.isOk());
 }
 
 std::vector<NamedModel> getNamedModels(const FilterFn& filter) {
@@ -499,6 +816,12 @@
 // 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 {};
 
@@ -510,6 +833,14 @@
     Execute(kDevice, kTestModel, /*testKind=*/TestKind::DYNAMIC_SHAPE);
 }
 
+TEST_P(MemoryDomainTest, Test) {
+    Execute(kDevice, kTestModel, /*testKind=*/TestKind::MEMORY_DOMAIN);
+}
+
+TEST_P(FencedComputeTest, Test) {
+    Execute(kDevice, kTestModel, /*testKind=*/TestKind::FENCED_COMPUTE);
+}
+
 TEST_P(QuantizationCouplingTest, Test) {
     Execute(kDevice, kTestModel, /*testKind=*/TestKind::QUANTIZATION_COUPLING);
 }
@@ -517,7 +848,14 @@
 INSTANTIATE_GENERATED_TEST(GeneratedTest,
                            [](const TestModel& testModel) { return !testModel.expectFailure; });
 
-INSTANTIATE_GENERATED_TEST(DynamicOutputShapeTest,
+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) {
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
index ad6323f..e597fac 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
@@ -36,6 +36,7 @@
     void SetUp() override;
     const sp<IDevice> kDevice = getData(std::get<NamedDevice>(GetParam()));
     const test_helper::TestModel& kTestModel = *getData(std::get<NamedModel>(GetParam()));
+    std::pair<bool, bool> mSupportsDeadlines;
 };
 
 using FilterFn = std::function<bool(const test_helper::TestModel&)>;
@@ -62,13 +63,17 @@
     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
 };
 
-void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel,
+void EvaluatePreparedModel(const sp<IDevice>& device, const sp<IPreparedModel>& preparedModel,
                            const test_helper::TestModel& testModel, TestKind testKind);
 
 }  // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
new file mode 100644
index 0000000..62ffcda
--- /dev/null
+++ b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
@@ -0,0 +1,299 @@
+/*
+ * 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 "1.0/Utils.h"
+#include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
+#include "GeneratedTestHarness.h"
+#include "Utils.h"
+
+namespace android::hardware::neuralnetworks::V1_3::vts::functional {
+
+using implementation::ExecutionCallback;
+using implementation::PreparedModelCallback;
+using test_helper::TestBuffer;
+using test_helper::TestModel;
+using V1_1::ExecutionPreference;
+using V1_2::MeasureTiming;
+using V1_2::OutputShape;
+using V1_2::Timing;
+
+using HidlToken =
+        hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
+
+enum class DeadlineBoundType { NOW, UNLIMITED };
+constexpr std::array<DeadlineBoundType, 2> deadlineBounds = {DeadlineBoundType::NOW,
+                                                             DeadlineBoundType::UNLIMITED};
+std::string toString(DeadlineBoundType type) {
+    switch (type) {
+        case DeadlineBoundType::NOW:
+            return "NOW";
+        case DeadlineBoundType::UNLIMITED:
+            return "UNLIMITED";
+    }
+    LOG(FATAL) << "Unrecognized DeadlineBoundType: " << static_cast<int>(type);
+    return {};
+}
+
+using Results = std::tuple<ErrorStatus, hidl_vec<OutputShape>, Timing>;
+using MaybeResults = std::optional<Results>;
+
+using ExecutionFunction =
+        std::function<MaybeResults(const sp<IPreparedModel>& preparedModel, const Request& request,
+                                   DeadlineBoundType deadlineBound)>;
+
+static OptionalTimePoint makeOptionalTimePoint(DeadlineBoundType deadlineBoundType) {
+    OptionalTimePoint deadline;
+    switch (deadlineBoundType) {
+        case DeadlineBoundType::NOW: {
+            const auto currentTime = std::chrono::steady_clock::now();
+            const auto currentTimeInNanoseconds =
+                    std::chrono::time_point_cast<std::chrono::nanoseconds>(currentTime);
+            const uint64_t nanosecondsSinceEpoch =
+                    currentTimeInNanoseconds.time_since_epoch().count();
+            deadline.nanoseconds(nanosecondsSinceEpoch);
+        } break;
+        case DeadlineBoundType::UNLIMITED: {
+            uint64_t unlimited = std::numeric_limits<uint64_t>::max();
+            deadline.nanoseconds(unlimited);
+        } break;
+    }
+    return deadline;
+}
+
+void runPrepareModelTest(const sp<IDevice>& device, const Model& model, Priority priority,
+                         std::optional<DeadlineBoundType> deadlineBound) {
+    OptionalTimePoint deadline;
+    if (deadlineBound.has_value()) {
+        deadline = makeOptionalTimePoint(deadlineBound.value());
+    }
+
+    // see if service can handle model
+    bool fullySupportsModel = false;
+    const Return<void> supportedCall = device->getSupportedOperations_1_3(
+            model, [&fullySupportsModel](ErrorStatus status, const hidl_vec<bool>& supported) {
+                ASSERT_EQ(ErrorStatus::NONE, status);
+                ASSERT_NE(0ul, supported.size());
+                fullySupportsModel = std::all_of(supported.begin(), supported.end(),
+                                                 [](bool valid) { return valid; });
+            });
+    ASSERT_TRUE(supportedCall.isOk());
+
+    // launch prepare model
+    const sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
+    const Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_3(
+            model, ExecutionPreference::FAST_SINGLE_ANSWER, priority, deadline,
+            hidl_vec<hidl_handle>(), hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
+    ASSERT_TRUE(prepareLaunchStatus.isOk());
+    ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
+
+    // retrieve prepared model
+    preparedModelCallback->wait();
+    const ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
+    const sp<V1_0::IPreparedModel> preparedModelV1_0 = preparedModelCallback->getPreparedModel();
+    const sp<IPreparedModel> preparedModel =
+            IPreparedModel::castFrom(preparedModelV1_0).withDefault(nullptr);
+
+    // The getSupportedOperations_1_3 call returns a list of operations that are
+    // guaranteed not to fail if prepareModel_1_3 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:
+                // If the execution was launched with a deadline of NOW, the
+                // deadline has already passed when the driver would launch the
+                // execution. In this case, the driver must return
+                // MISSED_DEADLINE_*.
+                EXPECT_TRUE(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 sp<IDevice>& device, const Model& model,
+                          bool supportsPrepareModelDeadline) {
+    // test priority
+    for (auto priority : hidl_enum_range<Priority>{}) {
+        SCOPED_TRACE("priority: " + toString(priority));
+        if (priority == kDefaultPriority) continue;
+        runPrepareModelTest(device, model, priority, {});
+    }
+
+    // test deadline
+    if (supportsPrepareModelDeadline) {
+        for (auto deadlineBound : deadlineBounds) {
+            SCOPED_TRACE("deadlineBound: " + toString(deadlineBound));
+            runPrepareModelTest(device, model, kDefaultPriority, deadlineBound);
+        }
+    }
+}
+
+static MaybeResults executeAsynchronously(const sp<IPreparedModel>& preparedModel,
+                                          const Request& request, DeadlineBoundType deadlineBound) {
+    SCOPED_TRACE("asynchronous");
+    const MeasureTiming measure = MeasureTiming::NO;
+    const OptionalTimePoint deadline = makeOptionalTimePoint(deadlineBound);
+
+    // launch execution
+    const sp<ExecutionCallback> callback = new ExecutionCallback();
+    Return<ErrorStatus> ret = preparedModel->execute_1_3(request, measure, deadline, callback);
+    EXPECT_TRUE(ret.isOk());
+    EXPECT_EQ(ErrorStatus::NONE, ret.withDefault(ErrorStatus::GENERAL_FAILURE));
+    if (!ret.isOk() || ret != ErrorStatus::NONE) return std::nullopt;
+
+    // retrieve execution results
+    callback->wait();
+    const ErrorStatus status = callback->getStatus();
+    hidl_vec<OutputShape> outputShapes = callback->getOutputShapes();
+    const Timing timing = callback->getTiming();
+
+    // return results
+    return Results{status, std::move(outputShapes), timing};
+}
+
+static MaybeResults executeSynchronously(const sp<IPreparedModel>& preparedModel,
+                                         const Request& request, DeadlineBoundType deadlineBound) {
+    SCOPED_TRACE("synchronous");
+    const MeasureTiming measure = MeasureTiming::NO;
+    const OptionalTimePoint deadline = makeOptionalTimePoint(deadlineBound);
+
+    // configure results callback
+    MaybeResults results;
+    const auto cb = [&results](const auto&... args) { *results = {args...}; };
+
+    // run execution
+    const Return<void> ret =
+            preparedModel->executeSynchronously_1_3(request, measure, deadline, cb);
+    EXPECT_TRUE(ret.isOk());
+    if (!ret.isOk()) return std::nullopt;
+
+    // return results
+    return results;
+}
+
+void runExecutionTest(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
+                      const Request& request, bool synchronous, DeadlineBoundType deadlineBound) {
+    const ExecutionFunction execute = synchronous ? executeSynchronously : executeAsynchronously;
+
+    // Perform execution and unpack results.
+    const auto results = execute(preparedModel, request, deadlineBound);
+    if (!results.has_value()) return;
+    const auto& [status, outputShapes, timing] = results.value();
+
+    // Verify no timing information was returned
+    EXPECT_EQ(UINT64_MAX, timing.timeOnDevice);
+    EXPECT_EQ(UINT64_MAX, timing.timeInDriver);
+
+    // Validate deadline information if applicable.
+    switch (deadlineBound) {
+        case DeadlineBoundType::NOW:
+            // If the execution was launched with a deadline of NOW, the
+            // deadline has already passed when the driver would launch the
+            // execution. In this case, the driver must return
+            // MISSED_DEADLINE_*.
+            ASSERT_TRUE(status == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+                        status == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
+            return;
+        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.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 = testModel.operands[testModel.outputIndexes[i]].dimensions;
+        const std::vector<uint32_t> actual = outputShapes[i].dimensions;
+        EXPECT_EQ(expect, actual);
+    }
+
+    // Retrieve execution results.
+    ASSERT_TRUE(nn::compliantWithV1_0(request));
+    const V1_0::Request request10 = nn::convertToV1_0(request);
+    const std::vector<TestBuffer> outputs = getOutputBuffers(request10);
+
+    // We want "close-enough" results.
+    checkResults(testModel, outputs);
+}
+
+void runExecutionTests(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
+                       const Request& request) {
+    for (bool synchronous : {false, true}) {
+        for (auto deadlineBound : deadlineBounds) {
+            runExecutionTest(preparedModel, testModel, request, synchronous, deadlineBound);
+        }
+    }
+}
+
+void runTests(const sp<IDevice>& device, const TestModel& testModel,
+              std::pair<bool, bool> supportsDeadlines) {
+    // setup
+    const auto [supportsPrepareModelDeadline, supportsExecutionDeadline] = supportsDeadlines;
+    if (!supportsPrepareModelDeadline && !supportsExecutionDeadline) return;
+    const Model model = createModel(testModel);
+
+    // run prepare model tests
+    runPrepareModelTests(device, model, supportsPrepareModelDeadline);
+
+    if (supportsExecutionDeadline) {
+        // prepare model
+        sp<IPreparedModel> preparedModel;
+        createPreparedModel(device, model, &preparedModel);
+        if (preparedModel == nullptr) return;
+
+        // run execution tests
+        const Request request = nn::convertToV1_3(createRequest(testModel));
+        runExecutionTests(preparedModel, testModel, request);
+    }
+}
+
+class DeadlineTest : public GeneratedTestBase {};
+
+TEST_P(DeadlineTest, Test) {
+    runTests(kDevice, kTestModel, mSupportsDeadlines);
+}
+
+INSTANTIATE_GENERATED_TEST(DeadlineTest,
+                           [](const TestModel& testModel) { return !testModel.expectFailure; });
+
+}  // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/Utils.cpp b/neuralnetworks/1.3/vts/functional/Utils.cpp
new file mode 100644
index 0000000..23e2af8
--- /dev/null
+++ b/neuralnetworks/1.3/vts/functional/Utils.cpp
@@ -0,0 +1,27 @@
+/*
+ * 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 "1.3/Utils.h"
+
+#include <iostream>
+
+namespace android::hardware::neuralnetworks::V1_3 {
+
+::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus) {
+    return os << toString(errorStatus);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3
diff --git a/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp b/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp
index 7df8046..6ff9dfd 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp
@@ -34,7 +34,6 @@
 using nn::ExecutionBurstController;
 using nn::RequestChannelSender;
 using nn::ResultChannelReceiver;
-using V1_0::ErrorStatus;
 using V1_0::Request;
 using V1_2::FmqRequestDatum;
 using V1_2::FmqResultDatum;
@@ -80,16 +79,17 @@
     ASSERT_NE(nullptr, fmqResultDescriptor);
 
     // configure burst
-    ErrorStatus errorStatus;
+    V1_0::ErrorStatus errorStatus;
     sp<IBurstContext> burstContext;
     const Return<void> ret = preparedModel->configureExecutionBurst(
             callback, *fmqRequestDescriptor, *fmqResultDescriptor,
-            [&errorStatus, &burstContext](ErrorStatus status, const sp<IBurstContext>& context) {
+            [&errorStatus, &burstContext](V1_0::ErrorStatus status,
+                                          const sp<IBurstContext>& context) {
                 errorStatus = status;
                 burstContext = context;
             });
     ASSERT_TRUE(ret.isOk());
-    ASSERT_EQ(ErrorStatus::NONE, errorStatus);
+    ASSERT_EQ(V1_0::ErrorStatus::NONE, errorStatus);
     ASSERT_NE(nullptr, burstContext.get());
 
     // return values
@@ -144,7 +144,7 @@
     auto results = receiver->getBlocking();
     ASSERT_TRUE(results.has_value());
     const auto [status, outputShapes, timing] = std::move(*results);
-    EXPECT_NE(ErrorStatus::NONE, status);
+    EXPECT_NE(V1_0::ErrorStatus::NONE, status);
     EXPECT_EQ(0u, outputShapes.size());
     EXPECT_TRUE(badTiming(timing));
 }
@@ -302,14 +302,15 @@
     // collect serialized result by running regular burst
     const auto [nRegular, outputShapesRegular, timingRegular, fallbackRegular] =
             controllerRegular->compute(request, MeasureTiming::NO, keys);
-    const ErrorStatus statusRegular = nn::convertResultCodeToErrorStatus(nRegular);
+    const V1_0::ErrorStatus statusRegular =
+            nn::convertToV1_0(nn::convertResultCodeToErrorStatus(nRegular));
     EXPECT_FALSE(fallbackRegular);
 
     // skip test if regular burst output isn't useful for testing a failure
     // caused by having too small of a length for the result FMQ
     const std::vector<FmqResultDatum> serialized =
             android::nn::serialize(statusRegular, outputShapesRegular, timingRegular);
-    if (statusRegular != ErrorStatus::NONE ||
+    if (statusRegular != V1_0::ErrorStatus::NONE ||
         serialized.size() <= kExecutionBurstChannelSmallLength) {
         return;
     }
@@ -318,8 +319,9 @@
     // large enough to return the serialized result
     const auto [nSmall, outputShapesSmall, timingSmall, fallbackSmall] =
             controllerSmall->compute(request, MeasureTiming::NO, keys);
-    const ErrorStatus statusSmall = nn::convertResultCodeToErrorStatus(nSmall);
-    EXPECT_NE(ErrorStatus::NONE, statusSmall);
+    const V1_0::ErrorStatus statusSmall =
+            nn::convertToV1_0(nn::convertResultCodeToErrorStatus(nSmall));
+    EXPECT_NE(V1_0::ErrorStatus::NONE, statusSmall);
     EXPECT_EQ(0u, outputShapesSmall.size());
     EXPECT_TRUE(badTiming(timingSmall));
     EXPECT_FALSE(fallbackSmall);
diff --git a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
index 8395111..1245432 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
@@ -18,14 +18,13 @@
 
 #include "1.0/Utils.h"
 #include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
 #include "GeneratedTestHarness.h"
 #include "VtsHalNeuralnetworks.h"
 
 namespace android::hardware::neuralnetworks::V1_3::vts::functional {
 
 using implementation::PreparedModelCallback;
-using V1_0::ErrorStatus;
-using V1_0::OperandLifeTime;
 using V1_1::ExecutionPreference;
 using V1_2::SymmPerChannelQuantParams;
 using HidlToken =
@@ -45,13 +44,19 @@
 }
 
 static void validatePrepareModel(const sp<IDevice>& device, const std::string& message,
-                                 const Model& model, ExecutionPreference preference) {
+                                 const Model& model, ExecutionPreference preference,
+                                 bool testDeadline) {
     SCOPED_TRACE(message + " [prepareModel_1_3]");
 
+    OptionalTimePoint deadline;
+    if (testDeadline) {
+        deadline.nanoseconds(std::numeric_limits<uint64_t>::max());
+    }
+
     sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
-    Return<ErrorStatus> prepareLaunchStatus =
-            device->prepareModel_1_3(model, preference, hidl_vec<hidl_handle>(),
-                                     hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
+    Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_3(
+            model, preference, kDefaultPriority, deadline, hidl_vec<hidl_handle>(),
+            hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
     ASSERT_TRUE(prepareLaunchStatus.isOk());
     ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(prepareLaunchStatus));
 
@@ -74,31 +79,32 @@
 // to the model does not leave this function.
 static void validate(const sp<IDevice>& device, const std::string& message, Model model,
                      const std::function<void(Model*)>& mutation,
-                     ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER) {
+                     ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER,
+                     bool testDeadline = false) {
     mutation(&model);
-    if (validExecutionPreference(preference)) {
+    if (validExecutionPreference(preference) && !testDeadline) {
         validateGetSupportedOperations(device, message, model);
     }
-    validatePrepareModel(device, message, model, preference);
+    validatePrepareModel(device, message, model, preference, testDeadline);
 }
 
 static uint32_t addOperand(Model* model) {
-    return hidl_vec_push_back(&model->operands,
+    return hidl_vec_push_back(&model->main.operands,
                               {
                                       .type = OperandType::INT32,
                                       .dimensions = {},
                                       .numberOfConsumers = 0,
                                       .scale = 0.0f,
                                       .zeroPoint = 0,
-                                      .lifetime = OperandLifeTime::MODEL_INPUT,
+                                      .lifetime = OperandLifeTime::SUBGRAPH_INPUT,
                                       .location = {.poolIndex = 0, .offset = 0, .length = 0},
                               });
 }
 
 static uint32_t addOperand(Model* model, OperandLifeTime lifetime) {
     uint32_t index = addOperand(model);
-    model->operands[index].numberOfConsumers = 1;
-    model->operands[index].lifetime = lifetime;
+    model->main.operands[index].numberOfConsumers = 1;
+    model->main.operands[index].lifetime = lifetime;
     return index;
 }
 
@@ -112,13 +118,13 @@
 };
 
 static void mutateOperandTypeTest(const sp<IDevice>& device, const Model& model) {
-    for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+    for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
         for (uint32_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) {
-                model->operands[operand].type = static_cast<OperandType>(invalidOperandType);
+                model->main.operands[operand].type = static_cast<OperandType>(invalidOperandType);
             });
         }
     }
@@ -150,15 +156,15 @@
 }
 
 static void mutateOperandRankTest(const sp<IDevice>& device, const Model& model) {
-    for (size_t operand = 0; operand < model.operands.size(); ++operand) {
-        const uint32_t invalidRank = getInvalidRank(model.operands[operand].type);
+    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) {
-            model->operands[operand].dimensions = std::vector<uint32_t>(invalidRank, 0);
+            model->main.operands[operand].dimensions = std::vector<uint32_t>(invalidRank, 0);
         });
     }
 }
@@ -190,12 +196,12 @@
 }
 
 static void mutateOperandScaleTest(const sp<IDevice>& device, const Model& model) {
-    for (size_t operand = 0; operand < model.operands.size(); ++operand) {
-        const float invalidScale = getInvalidScale(model.operands[operand].type);
+    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) {
-            model->operands[operand].scale = invalidScale;
+            model->main.operands[operand].scale = invalidScale;
         });
     }
 }
@@ -229,15 +235,15 @@
 }
 
 static void mutateOperandZeroPointTest(const sp<IDevice>& device, const Model& model) {
-    for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+    for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
         const std::vector<int32_t> invalidZeroPoints =
-                getInvalidZeroPoints(model.operands[operand].type);
+                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) {
-                model->operands[operand].zeroPoint = invalidZeroPoint;
+                model->main.operands[operand].zeroPoint = invalidZeroPoint;
             });
         }
     }
@@ -310,11 +316,11 @@
 
 static bool mutateOperationOperandTypeSkip(size_t operand, OperandType type, const Model& model) {
     // Do not test OEM types
-    if (type == model.operands[operand].type || type == OperandType::OEM ||
+    if (type == model.main.operands[operand].type || type == OperandType::OEM ||
         type == OperandType::TENSOR_OEM_BYTE) {
         return true;
     }
-    for (const Operation& operation : model.operations) {
+    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
@@ -331,6 +337,7 @@
         // - 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]) {
@@ -393,6 +400,20 @@
                     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;
         }
@@ -401,7 +422,7 @@
 }
 
 static void mutateOperationOperandTypeTest(const sp<IDevice>& device, const Model& model) {
-    for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+    for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
         for (OperandType invalidOperandType : hidl_enum_range<OperandType>{}) {
             if (mutateOperationOperandTypeSkip(operand, invalidOperandType, model)) {
                 continue;
@@ -410,7 +431,7 @@
                                         std::to_string(operand) + " set to type " +
                                         toString(invalidOperandType);
             validate(device, message, model, [operand, invalidOperandType](Model* model) {
-                mutateOperand(&model->operands[operand], invalidOperandType);
+                mutateOperand(&model->main.operands[operand], invalidOperandType);
             });
         }
     }
@@ -425,13 +446,13 @@
 };
 
 static void mutateOperationTypeTest(const sp<IDevice>& device, const Model& model) {
-    for (size_t operation = 0; operation < model.operations.size(); ++operation) {
+    for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
         for (uint32_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) {
-                model->operations[operation].type =
+                model->main.operations[operation].type =
                         static_cast<OperationType>(invalidOperationType);
             });
         }
@@ -441,14 +462,14 @@
 ///////////////////////// VALIDATE MODEL OPERATION INPUT OPERAND INDEX /////////////////////////
 
 static void mutateOperationInputOperandIndexTest(const sp<IDevice>& device, const Model& model) {
-    for (size_t operation = 0; operation < model.operations.size(); ++operation) {
-        const uint32_t invalidOperand = model.operands.size();
-        for (size_t input = 0; input < model.operations[operation].inputs.size(); ++input) {
+    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) {
-                model->operations[operation].inputs[input] = invalidOperand;
+                model->main.operations[operation].inputs[input] = invalidOperand;
             });
         }
     }
@@ -457,14 +478,15 @@
 ///////////////////////// VALIDATE MODEL OPERATION OUTPUT OPERAND INDEX /////////////////////////
 
 static void mutateOperationOutputOperandIndexTest(const sp<IDevice>& device, const Model& model) {
-    for (size_t operation = 0; operation < model.operations.size(); ++operation) {
-        const uint32_t invalidOperand = model.operands.size();
-        for (size_t output = 0; output < model.operations[operation].outputs.size(); ++output) {
+    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) {
-                model->operations[operation].outputs[output] = invalidOperand;
+                model->main.operations[operation].outputs[output] = invalidOperand;
             });
         }
     }
@@ -485,17 +507,17 @@
 }
 
 static void removeOperand(Model* model, uint32_t index) {
-    hidl_vec_removeAt(&model->operands, index);
-    for (Operation& operation : model->operations) {
+    hidl_vec_removeAt(&model->main.operands, index);
+    for (Operation& operation : model->main.operations) {
         removeValueAndDecrementGreaterValues(&operation.inputs, index);
         removeValueAndDecrementGreaterValues(&operation.outputs, index);
     }
-    removeValueAndDecrementGreaterValues(&model->inputIndexes, index);
-    removeValueAndDecrementGreaterValues(&model->outputIndexes, index);
+    removeValueAndDecrementGreaterValues(&model->main.inputIndexes, index);
+    removeValueAndDecrementGreaterValues(&model->main.outputIndexes, index);
 }
 
 static bool removeOperandSkip(size_t operand, const Model& model) {
-    for (const Operation& operation : model.operations) {
+    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) {
@@ -520,7 +542,7 @@
 }
 
 static void removeOperandTest(const sp<IDevice>& device, const Model& model) {
-    for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+    for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
         if (removeOperandSkip(operand, model)) {
             continue;
         }
@@ -533,14 +555,14 @@
 ///////////////////////// REMOVE OPERATION /////////////////////////
 
 static void removeOperation(Model* model, uint32_t index) {
-    for (uint32_t operand : model->operations[index].inputs) {
-        model->operands[operand].numberOfConsumers--;
+    for (uint32_t operand : model->main.operations[index].inputs) {
+        model->main.operands[operand].numberOfConsumers--;
     }
-    hidl_vec_removeAt(&model->operations, index);
+    hidl_vec_removeAt(&model->main.operations, index);
 }
 
 static void removeOperationTest(const sp<IDevice>& device, const Model& model) {
-    for (size_t operation = 0; operation < model.operations.size(); ++operation) {
+    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) { removeOperation(model, operation); });
@@ -615,9 +637,9 @@
 }
 
 static void removeOperationInputTest(const sp<IDevice>& device, const Model& model) {
-    for (size_t operation = 0; operation < model.operations.size(); ++operation) {
-        for (size_t input = 0; input < model.operations[operation].inputs.size(); ++input) {
-            const Operation& op = model.operations[operation];
+    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;
             }
@@ -625,9 +647,9 @@
                                         std::to_string(operation) + ", input " +
                                         std::to_string(input);
             validate(device, message, model, [operation, input](Model* model) {
-                uint32_t operand = model->operations[operation].inputs[input];
-                model->operands[operand].numberOfConsumers--;
-                hidl_vec_removeAt(&model->operations[operation].inputs, input);
+                uint32_t operand = model->main.operations[operation].inputs[input];
+                model->main.operands[operand].numberOfConsumers--;
+                hidl_vec_removeAt(&model->main.operations[operation].inputs, input);
             });
         }
     }
@@ -636,13 +658,14 @@
 ///////////////////////// REMOVE OPERATION OUTPUT /////////////////////////
 
 static void removeOperationOutputTest(const sp<IDevice>& device, const Model& model) {
-    for (size_t operation = 0; operation < model.operations.size(); ++operation) {
-        for (size_t output = 0; output < model.operations[operation].outputs.size(); ++output) {
+    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) {
-                hidl_vec_removeAt(&model->operations[operation].outputs, output);
+                hidl_vec_removeAt(&model->main.operations[operation].outputs, output);
             });
         }
     }
@@ -669,15 +692,15 @@
 }
 
 static void addOperationInputTest(const sp<IDevice>& device, const Model& model) {
-    for (size_t operation = 0; operation < model.operations.size(); ++operation) {
-        if (addOperationInputSkip(model.operations[operation])) {
+    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) {
-            uint32_t index = addOperand(model, OperandLifeTime::MODEL_INPUT);
-            hidl_vec_push_back(&model->operations[operation].inputs, index);
-            hidl_vec_push_back(&model->inputIndexes, index);
+            uint32_t index = addOperand(model, OperandLifeTime::SUBGRAPH_INPUT);
+            hidl_vec_push_back(&model->main.operations[operation].inputs, index);
+            hidl_vec_push_back(&model->main.inputIndexes, index);
         });
     }
 }
@@ -685,13 +708,13 @@
 ///////////////////////// ADD OPERATION OUTPUT /////////////////////////
 
 static void addOperationOutputTest(const sp<IDevice>& device, const Model& model) {
-    for (size_t operation = 0; operation < model.operations.size(); ++operation) {
+    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) {
-            uint32_t index = addOperand(model, OperandLifeTime::MODEL_OUTPUT);
-            hidl_vec_push_back(&model->operations[operation].outputs, index);
-            hidl_vec_push_back(&model->outputIndexes, index);
+            uint32_t index = addOperand(model, OperandLifeTime::SUBGRAPH_OUTPUT);
+            hidl_vec_push_back(&model->main.operations[operation].outputs, index);
+            hidl_vec_push_back(&model->main.outputIndexes, index);
         });
     }
 }
@@ -713,9 +736,19 @@
     }
 }
 
+///////////////////////// DEADLINE /////////////////////////
+
+static void deadlineTest(const sp<IDevice>& device, const Model& model) {
+    const std::string message = "deadlineTest: deadline not supported";
+    const auto noop = [](Model*) {};
+    validate(device, message, model, noop, ExecutionPreference::FAST_SINGLE_ANSWER,
+             /*testDeadline=*/true);
+}
+
 ////////////////////////// ENTRY POINT //////////////////////////////
 
-void validateModel(const sp<IDevice>& device, const Model& model) {
+void validateModel(const sp<IDevice>& device, const Model& model,
+                   bool prepareModelDeadlineSupported) {
     mutateOperandTypeTest(device, model);
     mutateOperandRankTest(device, model);
     mutateOperandScaleTest(device, model);
@@ -731,6 +764,9 @@
     addOperationInputTest(device, model);
     addOperationOutputTest(device, model);
     mutateExecutionPreferenceTest(device, model);
+    if (!prepareModelDeadlineSupported) {
+        deadlineTest(device, model);
+    }
 }
 
 }  // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
index 8092d04..1ddd09c 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
@@ -16,9 +16,11 @@
 
 #define LOG_TAG "neuralnetworks_hidl_hal_test"
 
+#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h>
 #include <chrono>
+
 #include "1.0/Utils.h"
-#include "1.2/Callbacks.h"
+#include "1.3/Callbacks.h"
 #include "ExecutionBurstController.h"
 #include "GeneratedTestHarness.h"
 #include "TestHarness.h"
@@ -27,12 +29,10 @@
 
 namespace android::hardware::neuralnetworks::V1_3::vts::functional {
 
-using V1_0::ErrorStatus;
-using V1_0::Request;
+using implementation::ExecutionCallback;
 using V1_2::MeasureTiming;
 using V1_2::OutputShape;
 using V1_2::Timing;
-using V1_2::implementation::ExecutionCallback;
 
 ///////////////////////// UTILITY FUNCTIONS /////////////////////////
 
@@ -45,7 +45,8 @@
 // that use the request. Note that the request here is passed by value, and any
 // mutation to the request does not leave this function.
 static void validate(const sp<IPreparedModel>& preparedModel, const std::string& message,
-                     Request request, const std::function<void(Request*)>& mutation) {
+                     Request request, const std::function<void(Request*)>& mutation,
+                     bool testDeadline = false) {
     mutation(&request);
 
     // We'd like to test both with timing requested and without timing
@@ -58,13 +59,18 @@
     };
     MeasureTiming measure = (hash & 1) ? MeasureTiming::YES : MeasureTiming::NO;
 
+    OptionalTimePoint deadline;
+    if (testDeadline) {
+        deadline.nanoseconds(std::numeric_limits<uint64_t>::max());
+    }
+
     // asynchronous
     {
         SCOPED_TRACE(message + " [execute_1_3]");
 
         sp<ExecutionCallback> executionCallback = new ExecutionCallback();
         Return<ErrorStatus> executeLaunchStatus =
-                preparedModel->execute_1_3(request, measure, executionCallback);
+                preparedModel->execute_1_3(request, measure, deadline, executionCallback);
         ASSERT_TRUE(executeLaunchStatus.isOk());
         ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(executeLaunchStatus));
 
@@ -82,7 +88,7 @@
         SCOPED_TRACE(message + " [executeSynchronously_1_3]");
 
         Return<void> executeStatus = preparedModel->executeSynchronously_1_3(
-                request, measure,
+                request, measure, deadline,
                 [](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes,
                    const Timing& timing) {
                     ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
@@ -93,9 +99,13 @@
     }
 
     // burst
-    {
+    // TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2.
+    if (!testDeadline) {
         SCOPED_TRACE(message + " [burst]");
 
+        ASSERT_TRUE(nn::compliantWithV1_0(request));
+        V1_0::Request request10 = nn::convertToV1_0(request);
+
         // create burst
         std::shared_ptr<::android::nn::ExecutionBurstController> burst =
                 android::nn::ExecutionBurstController::create(preparedModel,
@@ -103,13 +113,13 @@
         ASSERT_NE(nullptr, burst.get());
 
         // create memory keys
-        std::vector<intptr_t> keys(request.pools.size());
+        std::vector<intptr_t> keys(request10.pools.size());
         for (size_t i = 0; i < keys.size(); ++i) {
-            keys[i] = reinterpret_cast<intptr_t>(&request.pools[i]);
+            keys[i] = reinterpret_cast<intptr_t>(&request10.pools[i]);
         }
 
         // execute and verify
-        const auto [n, outputShapes, timing, fallback] = burst->compute(request, measure, keys);
+        const auto [n, outputShapes, timing, fallback] = burst->compute(request10, measure, keys);
         const ErrorStatus status = nn::convertResultCodeToErrorStatus(n);
         EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, status);
         EXPECT_EQ(outputShapes.size(), 0);
@@ -117,7 +127,7 @@
         EXPECT_FALSE(fallback);
 
         // additional burst testing
-        if (request.pools.size() > 0) {
+        if (request10.pools.size() > 0) {
             // valid free
             burst->freeMemory(keys.front());
 
@@ -128,6 +138,22 @@
             burst->freeMemory(keys.front());
         }
     }
+
+    // dispatch
+    {
+        SCOPED_TRACE(message + " [executeFenced]");
+        Return<void> ret = preparedModel->executeFenced(
+                request, {}, MeasureTiming::NO,
+                [](ErrorStatus error, const hidl_handle& handle,
+                   const sp<IFencedExecutionCallback>& callback) {
+                    if (error != ErrorStatus::DEVICE_UNAVAILABLE) {
+                        ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
+                    }
+                    ASSERT_EQ(handle.getNativeHandle(), nullptr);
+                    ASSERT_EQ(callback, nullptr);
+                });
+        ASSERT_TRUE(ret.isOk());
+    }
 }
 
 ///////////////////////// REMOVE INPUT ////////////////////////////////////
@@ -150,17 +176,29 @@
     }
 }
 
+///////////////////////// DEADLINE ////////////////////////////////////
+
+static void deadlineTest(const sp<IPreparedModel>& preparedModel, const Request& request) {
+    const std::string message = "deadlineTest: deadline not supported";
+    const auto noop = [](Request*) {};
+    validate(preparedModel, message, request, noop, /*testDeadline=*/true);
+}
+
 ///////////////////////////// ENTRY POINT //////////////////////////////////
 
-void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request) {
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request,
+                     bool executionDeadlineSupported) {
     removeInputTest(preparedModel, request);
     removeOutputTest(preparedModel, request);
+    if (!executionDeadlineSupported) {
+        deadlineTest(preparedModel, request);
+    }
 }
 
 void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Request& request) {
     SCOPED_TRACE("Expecting request to fail [executeSynchronously_1_3]");
     Return<void> executeStatus = preparedModel->executeSynchronously_1_3(
-            request, MeasureTiming::NO,
+            request, MeasureTiming::NO, {},
             [](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
                 ASSERT_NE(ErrorStatus::NONE, error);
                 EXPECT_EQ(outputShapes.size(), 0);
diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
index 92d8fa7..c84f5b7 100644
--- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
@@ -23,16 +23,16 @@
 #include <utility>
 #include "1.0/Utils.h"
 #include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
 #include "GeneratedTestHarness.h"
 #include "TestHarness.h"
+#include "Utils.h"
 
 namespace android::hardware::neuralnetworks::V1_3::vts::functional {
 
 using HidlToken =
         hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
 using implementation::PreparedModelCallback;
-using V1_0::ErrorStatus;
-using V1_0::Request;
 using V1_1::ExecutionPreference;
 
 // internal helper function
@@ -55,8 +55,8 @@
     // launch prepare model
     const sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
     const Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_3(
-            model, ExecutionPreference::FAST_SINGLE_ANSWER, hidl_vec<hidl_handle>(),
-            hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
+            model, ExecutionPreference::FAST_SINGLE_ANSWER, kDefaultPriority, {},
+            hidl_vec<hidl_handle>(), hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
     ASSERT_TRUE(prepareLaunchStatus.isOk());
     ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
 
@@ -84,6 +84,7 @@
                   << std::endl;
         GTEST_SKIP();
     }
+
     ASSERT_EQ(ErrorStatus::NONE, prepareReturnStatus);
     ASSERT_NE(nullptr, preparedModel->get());
 }
@@ -122,30 +123,58 @@
 INSTANTIATE_DEVICE_TEST(NeuralnetworksHidlTest);
 
 // Forward declaration from ValidateModel.cpp
-void validateModel(const sp<IDevice>& device, const Model& model);
+void validateModel(const sp<IDevice>& device, const Model& model,
+                   bool prepareModelDeadlineSupported);
 // Forward declaration from ValidateRequest.cpp
-void validateRequest(const sp<IPreparedModel>& preparedModel, const V1_0::Request& request);
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request,
+                     bool executionDeadlineSupported);
 // Forward declaration from ValidateRequest.cpp
-void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const V1_0::Request& request);
+void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Request& request);
 // Forward declaration from ValidateBurst.cpp
 void validateBurst(const sp<IPreparedModel>& preparedModel, const V1_0::Request& request);
 
-void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request) {
-    validateModel(device, model);
+// Validate sync_fence handles for dispatch with valid input
+void validateExecuteFenced(const sp<IPreparedModel>& preparedModel, const Request& request) {
+    SCOPED_TRACE("Expecting request to fail [executeFenced]");
+    Return<void> ret_null =
+            preparedModel->executeFenced(request, {hidl_handle(nullptr)}, V1_2::MeasureTiming::NO,
+                                         [](ErrorStatus error, const hidl_handle& handle,
+                                            const sp<IFencedExecutionCallback>& callback) {
+                                             // TODO: fix this once sample driver impl is merged.
+                                             if (error != ErrorStatus::DEVICE_UNAVAILABLE) {
+                                                 ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
+                                             }
+                                             ASSERT_EQ(handle.getNativeHandle(), nullptr);
+                                             ASSERT_EQ(callback, nullptr);
+                                         });
+    ASSERT_TRUE(ret_null.isOk());
+}
+
+void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request,
+                        std::pair<bool, bool> supportsDeadlines) {
+    const auto [prepareModelDeadlineSupported, executionDeadlineSupported] = supportsDeadlines;
+    validateModel(device, model, prepareModelDeadlineSupported);
 
     // Create IPreparedModel.
     sp<IPreparedModel> preparedModel;
     createPreparedModel(device, model, &preparedModel);
     if (preparedModel == nullptr) return;
 
-    validateRequest(preparedModel, request);
-    validateBurst(preparedModel, request);
+    validateRequest(preparedModel, request, executionDeadlineSupported);
+    validateExecuteFenced(preparedModel, request);
+
+    // TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2.
+    ASSERT_TRUE(nn::compliantWithV1_0(request));
+    V1_0::Request request10 = nn::convertToV1_0(request);
+    validateBurst(preparedModel, request10);
 }
 
-void validateFailure(const sp<IDevice>& device, const Model& model, const Request& request) {
+void validateFailure(const sp<IDevice>& device, const Model& model, const Request& request,
+                     std::pair<bool, bool> supportsDeadlines) {
+    const bool prepareModelDeadlineSupported = supportsDeadlines.first;
     // TODO: Should this always succeed?
     //       What if the invalid input is part of the model (i.e., a parameter).
-    validateModel(device, model);
+    validateModel(device, model, prepareModelDeadlineSupported);
 
     // Create IPreparedModel.
     sp<IPreparedModel> preparedModel;
@@ -157,11 +186,11 @@
 
 TEST_P(ValidationTest, Test) {
     const Model model = createModel(kTestModel);
-    const Request request = createRequest(kTestModel);
+    const Request request = nn::convertToV1_3(createRequest(kTestModel));
     if (kTestModel.expectFailure) {
-        validateFailure(kDevice, model, request);
+        validateFailure(kDevice, model, request, mSupportsDeadlines);
     } else {
-        validateEverything(kDevice, model, request);
+        validateEverything(kDevice, model, request, mSupportsDeadlines);
     }
 }
 
diff --git a/neuralnetworks/1.3/vts/functional/include/1.3/Callbacks.h b/neuralnetworks/1.3/vts/functional/include/1.3/Callbacks.h
index fb19a84..e9dec2d 100644
--- a/neuralnetworks/1.3/vts/functional/include/1.3/Callbacks.h
+++ b/neuralnetworks/1.3/vts/functional/include/1.3/Callbacks.h
@@ -18,8 +18,11 @@
 #define ANDROID_HARDWARE_NEURALNETWORKS_V1_3_CALLBACKS_H
 
 #include <android-base/thread_annotations.h>
+#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
 #include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
 #include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.3/IExecutionCallback.h>
 #include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h>
 #include <hidl/Status.h>
 #include <condition_variable>
@@ -136,7 +139,7 @@
      * @param preparedModel Returned model that has been prepared for execution,
      *     nullptr if the model was unable to be prepared.
      */
-    Return<void> notify_1_3(V1_0::ErrorStatus status,
+    Return<void> notify_1_3(V1_3::ErrorStatus status,
                             const sp<V1_3::IPreparedModel>& preparedModel) override;
 
     /**
@@ -158,7 +161,7 @@
      *     - GENERAL_FAILURE if there is an unspecified error
      *     - INVALID_ARGUMENT if the input model is invalid
      */
-    V1_0::ErrorStatus getStatus() const;
+    ErrorStatus getStatus() const;
 
     /**
      * Retrieves the model that has been prepared for execution from the
@@ -173,13 +176,216 @@
     sp<V1_0::IPreparedModel> getPreparedModel() const;
 
   private:
+    Return<void> notifyInternal(ErrorStatus status, const sp<V1_0::IPreparedModel>& preparedModel);
+
     mutable std::mutex mMutex;
     mutable std::condition_variable mCondition;
     bool mNotified GUARDED_BY(mMutex) = false;
-    V1_0::ErrorStatus mErrorStatus = V1_0::ErrorStatus::GENERAL_FAILURE;
+    ErrorStatus mErrorStatus = ErrorStatus::GENERAL_FAILURE;
     sp<V1_0::IPreparedModel> mPreparedModel;
 };
 
+/**
+ * The ExecutionCallback class is used to receive the results of the execution
+ * from a task executing asynchronously with respect to the runtime. If a
+ * calling thread calls wait or get* on a ExecutionCallback object and the
+ * corresponding asynchronous task has not finished the execution, the calling
+ * thread will block until the asynchronous task has either called one of the
+ * notify* methods.
+ *
+ * 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 IPreparedModel::execute*.
+ */
+class ExecutionCallback : public IExecutionCallback {
+  public:
+    /**
+     * IExecutionCallback::notify marks the callback object with the return
+     * status of the asynchronous execution that held this callback and enables
+     * all prior and future wait calls on the ExecutionCallback object to
+     * proceed.
+     *
+     * One of the IExecutionCallback::notify* methods must be called on a given
+     * ExecutionCallback 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 launching the asynchronous task
+     *     (if the launch fails) or from the asynchronous task itself (if the
+     *     launch succeeds). Must be:
+     *     - NONE if the asynchronous execution was successful
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if there is an unspecified error
+     *     - OUTPUT_INSUFFICIENT_SIZE if provided output buffer is not large
+     *         enough to store the resultant values
+     *     - INVALID_ARGUMENT if the input request is invalid
+     */
+    Return<void> notify(V1_0::ErrorStatus status) override;
+
+    /**
+     * IExecutionCallback::notify_1_2 marks the callback object with the results
+     * (error status, dynamic output shapes, and timing information) of the
+     * asynchronous execution that held this callback and enables all prior and
+     * future wait calls on the ExecutionCallback object to proceed.
+     *
+     * One of the IExecutionCallback::notify* methods must be called on a given
+     * ExecutionCallback 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 launching the asynchronous task
+     *     (if the launch fails) or from the asynchronous task itself (if the
+     *     launch succeeds). 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
+     *     - OUTPUT_INSUFFICIENT_SIZE if at least one output operand buffer is
+     *         not large enough to store the corresponding output
+     *     - INVALID_ARGUMENT if one of the input arguments to prepareModel is
+     *         invalid
+     * @param outputShapes A list of shape information of model output operands.
+     *     The index into "outputShapes" corresponds to the index of the output
+     *     operand in the Request outputs vector. outputShapes must be empty
+     *     unless the status is either NONE or OUTPUT_INSUFFICIENT_SIZE.
+     * @param Timing Duration of execution. Unless MeasureTiming::YES was passed
+     *     when launching the execution and status is NONE, all times must be
+     *     reported as UINT64_MAX. A driver may choose to report any time as
+     *     UINT64_MAX, indicating that particular measurement is not available.
+     */
+    Return<void> notify_1_2(V1_0::ErrorStatus status,
+                            const hidl_vec<V1_2::OutputShape>& outputShapes,
+                            const V1_2::Timing& timing) override;
+
+    /**
+     * IExecutionCallback::notify_1_3 marks the callback object with the results
+     * (error status, dynamic output shapes, and timing information) of the
+     * asynchronous execution that held this callback and enables all prior and
+     * future wait calls on the ExecutionCallback object to proceed.
+     *
+     * One of the IExecutionCallback::notify* methods must be called on a given
+     * ExecutionCallback 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 launching the asynchronous task
+     *     (if the launch fails) or from the asynchronous task itself (if the
+     *     launch succeeds). 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
+     *     - OUTPUT_INSUFFICIENT_SIZE if at least one output operand buffer is
+     *         not large enough to store the corresponding output
+     *     - INVALID_ARGUMENT if one of the input arguments to prepareModel is
+     *         invalid
+     *     - MISSED_DEADLINE_* if the deadline was not met
+     * @param outputShapes A list of shape information of model output operands.
+     *     The index into "outputShapes" corresponds to the index of the output
+     *     operand in the Request outputs vector. outputShapes must be empty
+     *     unless the status is either NONE or OUTPUT_INSUFFICIENT_SIZE.
+     * @param Timing Duration of execution. Unless MeasureTiming::YES was passed
+     *     when launching the execution and status is NONE, all times must be
+     *     reported as UINT64_MAX. A driver may choose to report any time as
+     *     UINT64_MAX, indicating that particular measurement is not available.
+     */
+    Return<void> notify_1_3(V1_3::ErrorStatus status,
+                            const hidl_vec<V1_2::OutputShape>& outputShapes,
+                            const V1_2::Timing& timing) override;
+
+    /**
+     * ExecutionCallback::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 one of the IPreparedModel::execute* methods. If
+     * IPreparedModel::execute* (but not IPreparedModel::executeSynchronously*)
+     * has not finished asynchronously executing, this call will block until the
+     * asynchronous task notifies the object.
+     *
+     * @return status Error status returned from launching the asynchronous task
+     *     (if the launch fails) or from the asynchronous task itself (if the
+     *     launch succeeds). 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
+     *     - OUTPUT_INSUFFICIENT_SIZE if at least one output operand buffer is
+     *         not large enough to store the corresponding output
+     *     - INVALID_ARGUMENT if one of the input arguments to prepareModel is
+     *         invalid
+     *     - MISSED_DEADLINE_* if the deadline could not be met
+     */
+    V1_3::ErrorStatus getStatus() const;
+
+    /**
+     * Retrieves the error status returned from the asynchronous task launched
+     * by one of the IPreparedModel::execute* methods. If
+     * IPreparedModel::execute* (but not IPreparedModel::executeSynchronously*)
+     * has not finished asynchronously executing, this call will block until the
+     * asynchronous task notifies the object.
+     *
+     * If the asynchronous task was launched by IPreparedModel::execute, an
+     * empty vector will be returned.
+     *
+     * @return outputShapes A list of shape information of model output
+     *     operands. The index into "outputShapes" corresponds to the index of
+     *     the output operand in the Request outputs vector. outputShapes must
+     *     be empty unless the status is either NONE or
+     *     OUTPUT_INSUFFICIENT_SIZE. outputShaps may be empty if the status is
+     *     NONE and all model output operands are fully-specified at execution
+     *     time. outputShapes must have the same number of elements as the
+     *     number of model output operands if the status is
+     *     OUTPUT_INSUFFICIENT_SIZE, or if the status is NONE and the model has
+     *     at least one output operand that is not fully-specified.
+     */
+    const std::vector<V1_2::OutputShape>& getOutputShapes() const;
+
+    /**
+     * Retrieves the error status returned from the asynchronous task launched
+     * by one of the IPreparedModel::execute* methods. If
+     * IPreparedModel::execute* (but not IPreparedModel::executeSynchronously*)
+     * has not finished asynchronously executing, this call will block until the
+     * asynchronous task notifies the object.
+     *
+     * If the asynchronous task was launched by IPreparedModel::execute, every
+     * time must be UINT64_MAX.
+     *
+     * @return timing Duration of the execution. Every time must be UINT64_MAX
+     *     unless the status is NONE.
+     */
+    V1_2::Timing getTiming() const;
+
+  private:
+    /*
+     * ExecutionCallback::notifyInternal stores the results of the execution
+     * (status, output shapes, and timing information) in the ExecutionCallback
+     * object before any call to wait or get* return. It then enables all prior
+     * and future wait calls on the ExecutionCallback object to proceed.
+     */
+    Return<void> notifyInternal(V1_3::ErrorStatus errorStatus,
+                                hidl_vec<V1_2::OutputShape> outputShapes, V1_2::Timing timing);
+
+    // members
+    mutable std::mutex mMutex;
+    mutable std::condition_variable mCondition;
+    bool mNotified GUARDED_BY(mMutex) = false;
+    V1_3::ErrorStatus mErrorStatus = V1_3::ErrorStatus::GENERAL_FAILURE;
+    std::vector<V1_2::OutputShape> mOutputShapes = {};
+    V1_2::Timing mTiming = {};
+};
+
 }  // namespace android::hardware::neuralnetworks::V1_3::implementation
 
 #endif  // ANDROID_HARDWARE_NEURALNETWORKS_V1_3_CALLBACKS_H
diff --git a/neuralnetworks/1.3/vts/functional/include/1.3/Utils.h b/neuralnetworks/1.3/vts/functional/include/1.3/Utils.h
new file mode 100644
index 0000000..3661b66
--- /dev/null
+++ b/neuralnetworks/1.3/vts/functional/include/1.3/Utils.h
@@ -0,0 +1,36 @@
+/*
+ * 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_NEURALNETWORKS_V1_3_UTILS_H
+#define ANDROID_HARDWARE_NEURALNETWORKS_V1_3_UTILS_H
+
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <iosfwd>
+
+namespace android::hardware::neuralnetworks {
+
+inline constexpr V1_3::Priority kDefaultPriority = V1_3::Priority::MEDIUM;
+
+}  // namespace android::hardware::neuralnetworks
+
+namespace android::hardware::neuralnetworks::V1_3 {
+
+// pretty-print values for error messages
+::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus);
+
+}  // namespace android::hardware::neuralnetworks::V1_3
+
+#endif  // ANDROID_HARDWARE_NEURALNETWORKS_V1_3_UTILS_H
diff --git a/power/aidl/Android.bp b/power/aidl/Android.bp
new file mode 100644
index 0000000..2a6cf94
--- /dev/null
+++ b/power/aidl/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+aidl_interface {
+    name: "android.hardware.power",
+    vendor_available: true,
+    srcs: [
+        "android/hardware/power/*.aidl",
+    ],
+    stability: "vintf",
+    backend: {
+        java: {
+            platform_apis: true,
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+        },
+    },
+}
diff --git a/power/aidl/android/hardware/power/Boost.aidl b/power/aidl/android/hardware/power/Boost.aidl
new file mode 100644
index 0000000..162a36a
--- /dev/null
+++ b/power/aidl/android/hardware/power/Boost.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.power;
+
+@VintfStability
+@Backing(type="int")
+enum Boost {
+    /**
+     * This boost is set when user interacting with the device, for example,
+     * touchscreen events are incoming. CPU and GPU load may be expected soon,
+     * and it may be appropriate to raise speeds of CPU, memory bus etc.
+     * Note that this is different from INTERACTIVE mode, which only indicates
+     * that such interaction *may* occur, not that it is actively occurring.
+     */
+    INTERACTION,
+
+    /**
+     * Below hints are currently not sent in Android framework but OEM might choose to
+     * implement for power/perf optimizations.
+     */
+
+    /**
+     * This boost indicates that the device is interacting with ML accelerator.
+     */
+    ML_ACC,
+
+    /**
+     * This boost indicates that the device is setting up audio stream.
+     */
+    AUDIO_LAUNCH,
+
+    /**
+     * This boost indicates that camera is being launched.
+     */
+    CAMERA_LAUNCH,
+
+    /**
+     * This boost indicates that camera shot is being taken.
+     */
+    CAMERA_SHOT,
+}
diff --git a/power/aidl/android/hardware/power/IPower.aidl b/power/aidl/android/hardware/power/IPower.aidl
new file mode 100644
index 0000000..9fb3fc0
--- /dev/null
+++ b/power/aidl/android/hardware/power/IPower.aidl
@@ -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.
+ */
+
+package android.hardware.power;
+
+import android.hardware.power.Boost;
+import android.hardware.power.Mode;
+
+@VintfStability
+interface IPower {
+    /**
+     * setMode() is called to enable/disable specific hint mode, which
+     * may result in adjustment of power/performance parameters of the
+     * cpufreq governor and other controls on device side.
+     *
+     * A particular platform may choose to ignore any mode hint.
+     *
+     * @param type Mode which is to be enable/disable.
+     * @param enabled true to enable, false to disable the mode.
+     */
+    oneway void setMode(in Mode type, in boolean enabled);
+
+    /**
+     * isModeSupported() is called to query if the given mode hint is
+     * supported by vendor.
+     *
+     * @return true if the hint passed is supported on this platform.
+     *         If false, setting the mode will have no effect.
+     * @param type Mode to be queried
+     */
+    boolean isModeSupported(in Mode type);
+
+   /**
+     * setBoost() indicates the device may need to boost some resources, as the
+     * the load is likely to increase before the kernel governors can react.
+     * Depending on the boost, it may be appropriate to raise the frequencies of
+     * CPU, GPU, memory subsystem, or stop CPU from going into deep sleep state.
+     * A particular platform may choose to ignore this hint.
+     *
+     * @param type Boost type which is to be set with a timeout.
+     * @param durationMs The expected duration of the user's interaction, if
+     *        known, or 0 if the expected duration is unknown.
+     *        a negative value indicates canceling previous boost.
+     *        A given platform can choose to boost some time based on durationMs,
+     *        and may also pick an appropriate timeout for 0 case.
+     */
+    oneway void setBoost(in Boost type, in int durationMs);
+
+    /**
+     * isBoostSupported() is called to query if the given boost hint is
+     * supported by vendor. When returns false, set the boost will have
+     * no effect on the platform.
+     *
+     * @return true if the hint passed is supported on this platform.
+     *         If false, setting the boost will have no effect.
+     * @param type Boost to be queried
+     */
+    boolean isBoostSupported(in Boost type);
+}
diff --git a/power/aidl/android/hardware/power/Mode.aidl b/power/aidl/android/hardware/power/Mode.aidl
new file mode 100644
index 0000000..9bb5b98
--- /dev/null
+++ b/power/aidl/android/hardware/power/Mode.aidl
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.power;
+
+@VintfStability
+@Backing(type="int")
+enum Mode {
+    /**
+     * This mode indicates that the device is to allow wake up when the
+     * screen is tapped twice.
+     */
+    DOUBLE_TAP_TO_WAKE,
+
+    /**
+     * This mode indidates Low power mode is activated or not. Low power
+     * mode is intended to save battery at the cost of performance.
+     */
+    LOW_POWER,
+
+    /**
+     * This mode indidates Sustained Performance mode is activated or not.
+     * Sustained performance mode is intended to provide a consistent level of
+     * performance for a prolonged amount of time.
+     */
+    SUSTAINED_PERFORMANCE,
+
+    /**
+     * This mode indidates VR Mode is activated or not. VR mode is intended
+     * to provide minimum guarantee for performance for the amount of time the
+     * device can sustain it.
+     */
+    VR,
+
+    /**
+     * This mode indicates that an application has been launched.
+     */
+    LAUNCH,
+
+    /**
+     * This mode indicates that the device is about to enter a period of
+     * expensive rendering.
+     */
+    EXPENSIVE_RENDERING,
+
+    /**
+     * This mode indicates that the device is about entering/leaving
+     * interactive state. (that is, the system is awake and ready for
+     * interaction, often with UI devices such as display and touchscreen
+     * enabled) or non-interactive state (the
+     * system appears asleep, display usually turned off). The
+     * non-interactive state may be entered after a period of
+     * inactivity in order to conserve battery power during
+     * such inactive periods.
+     *
+     * Typical actions are to turn on or off devices and adjust
+     * cpufreq parameters. This function may also call the
+     * appropriate interfaces to allow the kernel to suspend the
+     * system to low-power sleep state when entering non-interactive
+     * state, and to disallow low-power suspend when the system is in
+     * interactive state. When low-power suspend state is allowed, the
+     * kernel may suspend the system whenever no wakelocks are held.
+     */
+    INTERACTIVE,
+
+
+    /**
+     * Below hints are currently not sent in Android framework but OEM might choose to
+     * implement for power/perf optimizations.
+     */
+
+    /**
+     * This mode indicates that low latency audio is active.
+     */
+    AUDIO_STREAMING_LOW_LATENCY,
+
+    /**
+     * This hint indicates that camera secure stream is being started.
+     */
+    CAMERA_STREAMING_SECURE,
+
+    /**
+     * This hint indicates that camera low resolution stream is being started.
+     */
+    CAMERA_STREAMING_LOW,
+
+    /**
+     * This hint indicates that camera mid resolution stream is being started.
+     */
+    CAMERA_STREAMING_MID,
+
+    /**
+     * This hint indicates that camera high resolution stream is being started.
+     */
+    CAMERA_STREAMING_HIGH,
+}
diff --git a/power/aidl/default/Android.bp b/power/aidl/default/Android.bp
new file mode 100644
index 0000000..07cd368
--- /dev/null
+++ b/power/aidl/default/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+    name: "android.hardware.power-service.example",
+    relative_install_path: "hw",
+    init_rc: ["power-default.rc"],
+    vintf_fragments: ["power-default.xml"],
+    vendor: true,
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "android.hardware.power-ndk_platform",
+    ],
+    srcs: [
+        "main.cpp",
+        "Power.cpp",
+    ],
+}
diff --git a/power/aidl/default/Power.cpp b/power/aidl/default/Power.cpp
new file mode 100644
index 0000000..8610de3
--- /dev/null
+++ b/power/aidl/default/Power.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Power.h"
+
+#include <android-base/logging.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace example {
+
+ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
+    LOG(VERBOSE) << "Power setMode: " << static_cast<int32_t>(type) << " to: " << enabled;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Power::isModeSupported(Mode type, bool* _aidl_return) {
+    LOG(INFO) << "Power isModeSupported: " << static_cast<int32_t>(type);
+    *_aidl_return = false;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
+    LOG(VERBOSE) << "Power setBoost: " << static_cast<int32_t>(type)
+                 << ", duration: " << durationMs;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Power::isBoostSupported(Boost type, bool* _aidl_return) {
+    LOG(INFO) << "Power isBoostSupported: " << static_cast<int32_t>(type);
+    *_aidl_return = false;
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace example
+}  // namespace impl
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/power/aidl/default/Power.h b/power/aidl/default/Power.h
new file mode 100644
index 0000000..f7645aa
--- /dev/null
+++ b/power/aidl/default/Power.h
@@ -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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/power/BnPower.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace example {
+
+class Power : public BnPower {
+    ndk::ScopedAStatus setMode(Mode type, bool enabled) override;
+    ndk::ScopedAStatus isModeSupported(Mode type, bool* _aidl_return) override;
+    ndk::ScopedAStatus setBoost(Boost type, int32_t durationMs) override;
+    ndk::ScopedAStatus isBoostSupported(Boost type, bool* _aidl_return) override;
+};
+
+}  // namespace example
+}  // namespace impl
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/power/aidl/default/main.cpp b/power/aidl/default/main.cpp
new file mode 100644
index 0000000..964bd96
--- /dev/null
+++ b/power/aidl/default/main.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Power.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using aidl::android::hardware::power::impl::example::Power;
+
+int main() {
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    std::shared_ptr<Power> vib = ndk::SharedRefBase::make<Power>();
+
+    const std::string instance = std::string() + Power::descriptor + "/default";
+    binder_status_t status = AServiceManager_addService(vib->asBinder().get(), instance.c_str());
+    CHECK(status == STATUS_OK);
+
+    ABinderProcess_joinThreadPool();
+    return EXIT_FAILURE;  // should not reach
+}
diff --git a/power/aidl/default/power-default.rc b/power/aidl/default/power-default.rc
new file mode 100644
index 0000000..9efbc85
--- /dev/null
+++ b/power/aidl/default/power-default.rc
@@ -0,0 +1,4 @@
+service vendor.power-default /vendor/bin/hw/android.hardware.power-service.example
+    class hal
+    user nobody
+    group system
diff --git a/power/aidl/default/power-default.xml b/power/aidl/default/power-default.xml
new file mode 100644
index 0000000..caf6ea2
--- /dev/null
+++ b/power/aidl/default/power-default.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.power</name>
+        <fqname>IPower/default</fqname>
+    </hal>
+</manifest>
diff --git a/power/aidl/vts/Android.bp b/power/aidl/vts/Android.bp
new file mode 100644
index 0000000..7726fd8
--- /dev/null
+++ b/power/aidl/vts/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+    name: "VtsHalPowerTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: ["VtsHalPowerTargetTest.cpp"],
+    shared_libs: [
+        "libbinder",
+    ],
+    static_libs: [
+        "android.hardware.power-cpp",
+    ],
+    test_suites: [
+        "vts-core",
+    ],
+}
diff --git a/power/aidl/vts/VtsHalPowerTargetTest.cpp b/power/aidl/vts/VtsHalPowerTargetTest.cpp
new file mode 100644
index 0000000..c0e0858
--- /dev/null
+++ b/power/aidl/vts/VtsHalPowerTargetTest.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
+#include <future>
+
+using android::ProcessState;
+using android::sp;
+using android::String16;
+using android::binder::Status;
+using android::hardware::power::Boost;
+using android::hardware::power::IPower;
+using android::hardware::power::Mode;
+
+const std::vector<Boost> kBoosts{android::enum_range<Boost>().begin(),
+                                 android::enum_range<Boost>().end()};
+
+const std::vector<Mode> kModes{android::enum_range<Mode>().begin(),
+                               android::enum_range<Mode>().end()};
+
+const std::vector<Boost> kInvalidBoosts = {
+        static_cast<Boost>(static_cast<int32_t>(kBoosts.front()) - 1),
+        static_cast<Boost>(static_cast<int32_t>(kBoosts.back()) + 1),
+};
+
+const std::vector<Mode> kInvalidModes = {
+        static_cast<Mode>(static_cast<int32_t>(kModes.front()) - 1),
+        static_cast<Mode>(static_cast<int32_t>(kModes.back()) + 1),
+};
+
+class PowerAidl : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        power = android::waitForDeclaredService<IPower>(String16(GetParam().c_str()));
+        ASSERT_NE(power, nullptr);
+    }
+
+    sp<IPower> power;
+};
+
+TEST_P(PowerAidl, setMode) {
+    for (const auto& mode : kModes) {
+        ASSERT_TRUE(power->setMode(mode, true).isOk());
+        ASSERT_TRUE(power->setMode(mode, false).isOk());
+    }
+    for (const auto& mode : kInvalidModes) {
+        ASSERT_TRUE(power->setMode(mode, true).isOk());
+        ASSERT_TRUE(power->setMode(mode, false).isOk());
+    }
+}
+
+TEST_P(PowerAidl, isModeSupported) {
+    for (const auto& mode : kModes) {
+        bool supported;
+        ASSERT_TRUE(power->isModeSupported(mode, &supported).isOk());
+    }
+    for (const auto& mode : kInvalidModes) {
+        bool supported;
+        ASSERT_TRUE(power->isModeSupported(mode, &supported).isOk());
+        // Should return false for values outsides enum
+        ASSERT_FALSE(supported);
+    }
+}
+
+TEST_P(PowerAidl, setBoost) {
+    for (const auto& boost : kBoosts) {
+        ASSERT_TRUE(power->setBoost(boost, 0).isOk());
+        ASSERT_TRUE(power->setBoost(boost, 1000).isOk());
+        ASSERT_TRUE(power->setBoost(boost, -1).isOk());
+    }
+    for (const auto& boost : kInvalidBoosts) {
+        ASSERT_TRUE(power->setBoost(boost, 0).isOk());
+        ASSERT_TRUE(power->setBoost(boost, 1000).isOk());
+        ASSERT_TRUE(power->setBoost(boost, -1).isOk());
+    }
+}
+
+TEST_P(PowerAidl, isBoostSupported) {
+    for (const auto& boost : kBoosts) {
+        bool supported;
+        ASSERT_TRUE(power->isBoostSupported(boost, &supported).isOk());
+    }
+    for (const auto& boost : kInvalidBoosts) {
+        bool supported;
+        ASSERT_TRUE(power->isBoostSupported(boost, &supported).isOk());
+        // Should return false for values outsides enum
+        ASSERT_FALSE(supported);
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(Power, PowerAidl,
+                         testing::ValuesIn(android::getAidlHalInstanceNames(IPower::descriptor)),
+                         android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ProcessState::self()->startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/radio/1.5/IRadio.hal b/radio/1.5/IRadio.hal
index e41989c..bea0454 100644
--- a/radio/1.5/IRadio.hal
+++ b/radio/1.5/IRadio.hal
@@ -16,15 +16,17 @@
 
 package android.hardware.radio@1.5;
 
+import @1.0::CdmaSmsMessage;
 import @1.2::DataRequestReason;
-import @1.4::IRadio;
 import @1.4::DataProfileInfo;
+import @1.4::IRadio;
 import @1.5::AccessNetwork;
 import @1.5::BarringInfo;
 import @1.5::DataProfileInfo;
 import @1.5::IndicationFilter;
 import @1.5::LinkAddress;
 import @1.5::NetworkScanRequest;
+import @1.5::RadioAccessNetworks;
 import @1.5::RadioAccessSpecifier;
 import @1.5::SignalThresholdInfo;
 
@@ -245,4 +247,51 @@
      * Response callback is IRadioResponse.getBarringInfoResponse()
      */
     oneway getBarringInfo(int32_t serial);
+
+    /**
+     * Request current voice registration state
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioResponse.getVoiceRegistrationStateResponse_1_5()
+     */
+    oneway getVoiceRegistrationState_1_5(int32_t serial);
+
+    /**
+     * Request current data registration state
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioResponse.getDataRegistrationStateResponse_1_5()
+     */
+    oneway getDataRegistrationState_1_5(int32_t serial);
+
+    /*
+     * Manually select a specified network.
+     * This request must not respond until the new operator is selected and registered.
+     * Per TS 23.122, the RAN is just the initial suggested value.
+     * If registration fails, the RAN is not available afterwards, or the RAN is not within
+     * the network types specified by IRadio::setPreferredNetworkTypeBitmap, then the modem
+     * will need to select the next best RAN for network registration.
+     *
+     * @param serial Serial number of request.
+     * @param operatorNumeric String specifying MCCMNC of network to select (eg "310170").
+     * @param ran Initial suggested radio access network type. If value is UNKNOWN, the modem
+     *     will select the next best RAN for network registration.
+     *
+     * Response function is IRadioResponse.setNetworkSelectionModeManualResponse_1_5()
+     */
+    oneway setNetworkSelectionModeManual_1_5(int32_t serial, string operatorNumeric,
+            RadioAccessNetworks ran);
+
+    /**
+     * Send an SMS message. Identical to sendCdmaSms,
+     * except that more messages are expected to be sent soon.
+     *
+     * @param serial Serial number of request.
+     * @param sms Cdma Sms to be sent described by CdmaSmsMessage in types.hal
+     *
+     * Response callback is IRadioResponse.sendCdmaSMSExpectMoreResponse()
+     */
+    oneway sendCdmaSmsExpectMore(int32_t serial, CdmaSmsMessage sms);
 };
diff --git a/radio/1.5/IRadioIndication.hal b/radio/1.5/IRadioIndication.hal
index cafecbc..c40b473 100644
--- a/radio/1.5/IRadioIndication.hal
+++ b/radio/1.5/IRadioIndication.hal
@@ -76,4 +76,23 @@
      */
     oneway barringInfoChanged(
             RadioIndicationType type, CellIdentity cellIdentity, vec<BarringInfo> barringInfos);
+
+    /**
+     * Report all of the current cell information known to the radio.
+     *
+     * This indication is updated from IRadioIndication@1.4 to report the @1.5 version of
+     * CellInfo.
+     *
+     * @param type Type of radio indication
+     * @param records Current cell information
+     */
+    oneway cellInfoList_1_5(RadioIndicationType type, vec<CellInfo> records);
+
+    /**
+     * Incremental network scan results.
+     *
+     * This indication is updated from IRadioIndication@1.4 to report the @1.5 version of
+     * CellInfo.
+     */
+    oneway networkScanResult_1_5(RadioIndicationType type, NetworkScanResult result);
 };
diff --git a/radio/1.5/IRadioResponse.hal b/radio/1.5/IRadioResponse.hal
index 2ed789a..9fa521e 100644
--- a/radio/1.5/IRadioResponse.hal
+++ b/radio/1.5/IRadioResponse.hal
@@ -17,8 +17,11 @@
 package android.hardware.radio@1.5;
 
 import @1.0::RadioResponseInfo;
+import @1.0::SendSmsResult;
 import @1.4::IRadioResponse;
 import @1.5::BarringInfo;
+import @1.5::CellInfo;
+import @1.5::RegStateResult;
 import @1.5::SetupDataCallResult;
 
 /**
@@ -170,4 +173,99 @@
      *   RadioError:MODEM_ERR
      */
     oneway getBarringInfoResponse(RadioResponseInfo info, vec<BarringInfo> barringInfos);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param voiceRegResponse Current Voice registration response as defined by RegStateResult
+     *        in types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getVoiceRegistrationStateResponse_1_5(RadioResponseInfo info,
+            RegStateResult voiceRegResponse);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param dataRegResponse Current Data registration response as defined by RegStateResult in
+     *        types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:NOT_PROVISIONED
+     */
+    oneway getDataRegistrationStateResponse_1_5(RadioResponseInfo info,
+            RegStateResult dataRegResponse);
+
+    /**
+     * This is identitcal to getCellInfoListResponse_1_4 but uses an updated version of CellInfo.
+     *
+     * @param info Response info struct containing response type, serial no. and error
+     * @param cellInfo List of current cell information known to radio
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getCellInfoListResponse_1_5(RadioResponseInfo info, vec<CellInfo> cellInfo);
+
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:ILLEGAL_SIM_OR_ME
+     *   RadioError:OPERATION_NOT_ALLOWED
+     *   RadioError:INVALID_STATE
+     *   RadioError:NO_MEMORY
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:SYSTEM_ERR
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:MODEM_ERR
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:NO_RESOURCES
+     *   RadioError:CANCELLED
+     *
+     * Returns RadioError:ILLEGAL_SIM_OR_ME when the failure is permanent and
+     * no retries needed, such as illegal SIM or ME.
+     */
+    oneway setNetworkSelectionModeManualResponse_1_5(RadioResponseInfo info);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param sms Response to sms sent as defined by SendSmsResult in types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:SMS_SEND_FAIL_RETRY
+     *   RadioError:NETWORK_REJECT
+     *   RadioError:INVALID_STATE
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:NO_MEMORY
+     *   RadioError:REQUEST_RATE_LIMITED
+     *   RadioError:INVALID_SMS_FORMAT
+     *   RadioError:SYSTEM_ERR
+     *   RadioError:FDN_CHECK_FAILURE
+     *   RadioError:ENCODING_ERR
+     *   RadioError:INVALID_SMSC_ADDRESS
+     *   RadioError:MODEM_ERR
+     *   RadioError:NETWORK_ERR
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:INVALID_MODEM_STATE
+     *   RadioError:NETWORK_NOT_READY
+     *   RadioError:OPERATION_NOT_ALLOWED
+     *   RadioError:NO_RESOURCES
+     *   RadioError:CANCELLED
+     *   RadioError:SIM_ABSENT
+     */
+    oneway sendCdmaSmsExpectMoreResponse(RadioResponseInfo info, SendSmsResult sms);
 };
diff --git a/radio/1.5/types.hal b/radio/1.5/types.hal
index 724d014..82feced 100644
--- a/radio/1.5/types.hal
+++ b/radio/1.5/types.hal
@@ -16,26 +16,46 @@
 
 package android.hardware.radio@1.5;
 
+import @1.0::ApnAuthType;
+import @1.0::DataProfileId;
+import @1.0::DataProfileInfoType;
+import @1.0::CdmaSignalStrength;
+import @1.0::EvdoSignalStrength;
+import @1.0::GsmSignalStrength;
+import @1.0::LteSignalStrength;
+import @1.0::RadioAccessFamily;
+import @1.0::RadioError;
+import @1.0::RegState;
+import @1.0::TimeStampType;
 import @1.1::EutranBands;
 import @1.1::GeranBands;
 import @1.1::RadioAccessNetworks;
 import @1.1::RadioAccessSpecifier;
+import @1.1::ScanStatus;
 import @1.1::ScanType;
 import @1.1::UtranBands;
+import @1.2::CellConnectionStatus;
 import @1.2::CellIdentityCdma;
 import @1.2::CellIdentityGsm;
 import @1.2::CellIdentityWcdma;
 import @1.2::CellIdentityTdscdma;
 import @1.2::CellIdentityLte;
+import @1.2::CellInfoCdma;
 import @1.2::IndicationFilter;
 import @1.2::NetworkScanRequest;
+import @1.2::TdscdmaSignalStrength;
+import @1.2::WcdmaSignalStrength;
 import @1.4::AccessNetwork;
 import @1.4::ApnTypes;
 import @1.4::CellIdentityNr;
 import @1.4::DataCallFailCause;
 import @1.4::DataConnActiveStatus;
 import @1.4::DataProfileInfo;
+import @1.4::LteVopsInfo;
+import @1.4::NrIndicators;
+import @1.4::NrSignalStrength;
 import @1.4::PdpProtocolType;
+import @1.4::RadioTechnology;
 
 import android.hidl.safe_union@1.0::Monostate;
 
@@ -146,7 +166,11 @@
 };
 
 enum RadioAccessNetworks : @1.1::RadioAccessNetworks {
+    UNKNOWN = 0,
+    /** Next Generation Radio Access Network */
     NGRAN = 4,
+    /** CDMA 2000 Network */
+    CDMA2000 = 5,
 };
 
 /**
@@ -228,6 +252,16 @@
     BAND_261 = 261,
 };
 
+enum UtranBands : @1.1::UtranBands {
+    /** TD-SCDMA bands. 3GPP TS 25.102, Table 5.2: Frequency bands */
+    BAND_A = 101,
+    BAND_B = 102,
+    BAND_C = 103,
+    BAND_D = 104,
+    BAND_E = 105,
+    BAND_F = 106,
+};
+
 /**
  * Overwritten from @1.2::NetworkScanRequest to update
  * RadioAccessSpecifier to 1.5 version
@@ -293,13 +327,74 @@
 };
 
 /**
- * Extended from @1.4::DataProfileInfo to update ApnTypes to 1.5 version
+ * Extended from @1.4::DataProfileInfo to update ApnTypes to 1.5 version and replace mtu with
+ * mtuV4 and mtuV6. In the future, this must be extended instead of overwritten.
  */
 struct DataProfileInfo {
-    @1.4::DataProfileInfo base;
+
+    /** ID of the data profile. */
+    DataProfileId profileId;
+
+    /** The APN name. */
+    string apn;
+
+    /** PDP_type values. */
+    PdpProtocolType protocol;
+
+    /** PDP_type values used on roaming network. */
+    PdpProtocolType roamingProtocol;
+
+    /** APN authentication type. */
+    ApnAuthType authType;
+
+    /** The username for APN, or empty string. */
+    string user;
+
+    /** The password for APN, or empty string. */
+    string password;
+
+    /** Data profile technology type. */
+    DataProfileInfoType type;
+
+    /** The period in seconds to limit the maximum connections. */
+    int32_t maxConnsTime;
+
+    /** The maximum connections during maxConnsTime. */
+    int32_t maxConns;
+
+    /**
+     * The required wait time in seconds after a successful UE initiated disconnect of a given PDN
+     * connection before the device can send a new PDN connection request for that given PDN.
+     */
+    int32_t waitTime;
+
+    /** True to enable the profile, false to disable. */
+    bool enabled;
 
     /** Supported APN types bitmap. See ApnTypes for the value of each bit. */
     bitfield<ApnTypes> supportedApnTypesBitmap;
+
+    /** The bearer bitmap. See RadioAccessFamily for the value of each bit. */
+    bitfield<RadioAccessFamily> bearerBitmap;
+
+    /** Maximum transmission unit (MTU) size in bytes for IPv4. */
+    int32_t mtuV4;
+
+    /** Maximum transmission unit (MTU) size in bytes for IPv6. */
+    int32_t mtuV6;
+
+    /**
+     * True if this data profile was used to bring up the last default (i.e internet) data
+     * connection successfully.
+     */
+    bool preferred;
+
+    /**
+     * If true, modem must persist this data profile and profileId must not be
+     * set to DataProfileId.INVALID. If the same data profile exists, this data profile must
+     * overwrite it.
+     */
+    bool persistent;
 };
 
 /**
@@ -332,21 +427,26 @@
     bitfield<AddressProperty> properties;
 
     /**
-     * The UTC time that this link address will be deprecated. 0 indicates this information is not
-     * available.
+     * The time, as reported by SystemClock.elapsedRealtime(), when this link address will be or
+     * was deprecated. -1 indicates this information is not available. At the time existing
+     * connections can still use this address until it expires, but new connections should use the
+     * new address. LONG_MAX(0x7FFFFFFFFFFFFFFF) indicates this link address will never be
+     * deprecated.
      */
-    uint64_t deprecatedTime;
+    uint64_t deprecationTime;
 
     /**
-     * The UTC time that this link address will expire and no longer valid. 0 indicates this
-     * information is not available.
+     * The time, as reported by SystemClock.elapsedRealtime(), when this link address will expire
+     * and be removed from the interface. -1 indicates this information is not available.
+     * LONG_MAX(0x7FFFFFFFFFFFFFFF) indicates this link address will never expire.
      */
-    uint64_t expiredTime;
+    uint64_t expirationTime;
 };
 
 /**
  * Overwritten from @1.4::SetupDataCallResult in order to update the addresses to 1.5
- * version. In 1.5 the type of addresses changes to vector of LinkAddress.
+ * version. In 1.5 the type of addresses changes to vector of LinkAddress, and mtu is replaced by
+ * mtuV4 and mtuV6.
  */
 struct SetupDataCallResult {
     /** Data call fail cause. DataCallFailCause.NONE if no error. */
@@ -400,10 +500,16 @@
     vec<string> pcscf;
 
     /**
-     * MTU received from network. Value <= 0 means network has either not sent a value or sent an
-     * invalid value.
+     * MTU received from network for IPv4.
+     * Value <= 0 means network has either not sent a value or sent an invalid value.
      */
-    int32_t mtu;
+    int32_t mtuV4;
+
+    /**
+     * MTU received from network for IPv6.
+     * Value <= 0 means network has either not sent a value or sent an invalid value.
+     */
+    int32_t mtuV6;
 };
 
 enum Domain : int32_t {
@@ -414,6 +520,172 @@
     PS = 1 << 1,
 };
 
+struct ClosedSubscriberGroupInfo {
+    /**
+     * Indicates whether the cell is restricted to only CSG members. A cell not broadcasting the
+     * CSG Indication but reporting CSG information is considered a Hybrid Cell.
+     * Refer to the "csg-Indication" field in 3GPP TS 36.331 section 6.2.2
+     * SystemInformationBlockType1.
+     * Also refer to "CSG Indicator" in 3GPP TS 25.331 section 10.2.48.8.1 and TS 25.304.
+     */
+    bool csgIndication;
+
+    /**
+     * The human-readable name of the closed subscriber group operating this cell.
+     * Refer to "hnb-Name" in TS 36.331 section 6.2.2 SystemInformationBlockType9.
+     * Also refer to "HNB Name" in 3GPP TS25.331 section 10.2.48.8.23 and TS 23.003 section 4.8.
+     */
+    string homeNodebName;
+
+    /**
+     * The identity of the closed subscriber group that the cell belongs to.
+     * Refer to "CSG-Identity" in TS 36.336 section 6.3.4.
+     * Also refer to "CSG Identity" in 3GPP TS 25.331 section 10.3.2.8 and TS 23.003 section 4.7.
+     */
+    int32_t csgIdentity;
+};
+
+safe_union OptionalCsgInfo {
+    /**
+     * If no CSG info is provided by the cell, then this structure shall be present.
+     */
+    Monostate noinit;
+
+    /**
+     * If CSG info is provided by the cell, this structure shall be present.
+     */
+    ClosedSubscriberGroupInfo csgInfo;
+};
+
+struct CellIdentityGsm {
+    /**
+     * The fields "mcc" and "mnc" must contain the PLMN-ID of the primary PLMN of this cell.
+     */
+    @1.2::CellIdentityGsm base;
+
+    /** Additional PLMN-IDs beyond the primary PLMN broadcast for this cell */
+    vec<string> additionalPlmns;
+};
+
+struct CellIdentityWcdma {
+    /**
+     * The fields "mcc" and "mnc" must contain the PLMN-ID of the primary PLMN of this cell.
+     */
+    @1.2::CellIdentityWcdma base;
+
+    /** Additional PLMN-IDs beyond the primary PLMN broadcast for this cell */
+    vec<string> additionalPlmns;
+
+    /** Information about any closed subscriber group ID for this cell */
+    OptionalCsgInfo optionalCsgInfo;
+};
+
+struct CellIdentityTdscdma {
+    /**
+     * The fields "mcc" and "mnc" must contain the PLMN-ID of the primary PLMN of this cell.
+     */
+    @1.2::CellIdentityTdscdma base;
+
+    /** Additional PLMN-IDs beyond the primary PLMN broadcast for this cell */
+    vec<string> additionalPlmns;
+
+    /** Information about any closed subscriber group ID for this cell */
+    OptionalCsgInfo optionalCsgInfo;
+};
+
+struct CellIdentityLte {
+    /**
+     * The fields "mcc" and "mnc" must contain the PLMN-ID of the primary PLMN of this cell.
+     */
+    @1.2::CellIdentityLte base;
+
+    /** Additional PLMN-IDs beyond the primary PLMN broadcast for this cell */
+    vec<string> additionalPlmns;
+
+    /** Information about any closed subscriber group ID for this cell */
+    OptionalCsgInfo optionalCsgInfo;
+
+    /** Bands used by the cell. */
+    vec<EutranBands> bands;
+};
+
+/**
+ * The CellIdentity structure should be reported once for each element of the PLMN-IdentityInfoList
+ * broadcast in SIB1 CellAccessRelatedInfo as per 3GPP TS 38.331 Section 6.3.2.
+ */
+struct CellIdentityNr {
+    /**
+     * The fields "mcc" and "mnc" must contain the PLMN-ID of the primary PLMN of this cell.
+     */
+    @1.4::CellIdentityNr base;
+
+    /** Additional PLMN-IDs beyond the primary PLMN broadcast for this cell */
+    vec<string> additionalPlmns;
+
+    /** Bands used by the cell. */
+    vec<NgranBands> bands;
+};
+
+struct CellInfoGsm {
+    CellIdentityGsm cellIdentityGsm;
+    GsmSignalStrength signalStrengthGsm;
+};
+
+struct CellInfoWcdma {
+    CellIdentityWcdma cellIdentityWcdma;
+    WcdmaSignalStrength signalStrengthWcdma;
+};
+
+struct CellInfoTdscdma {
+    CellIdentityTdscdma cellIdentityTdscdma;
+    TdscdmaSignalStrength signalStrengthTdscdma;
+};
+
+struct CellInfoLte {
+    CellIdentityLte cellIdentityLte;
+    LteSignalStrength signalStrengthLte;
+};
+
+struct CellInfoNr {
+    CellIdentityNr cellIdentityNr;
+    NrSignalStrength signalStrengthNr;
+};
+
+struct CellInfo {
+    /**
+     * True if this cell is registered false if not registered.
+     */
+    bool registered;
+    /**
+     * Type of time stamp represented by timeStamp.
+     */
+    TimeStampType timeStampType;
+    /**
+     * Time in nanos as returned by ril_nano_time.
+     */
+    uint64_t timeStamp;
+    /**
+     * Connection status for the cell.
+     */
+    CellConnectionStatus connectionStatus;
+
+    safe_union CellInfoRatSpecificInfo {
+        /**
+         * 3gpp CellInfo types.
+         */
+        CellInfoGsm gsm;
+        CellInfoWcdma wcdma;
+        CellInfoTdscdma tdscdma;
+        CellInfoLte lte;
+        CellInfoNr nr;
+
+        /**
+         * 3gpp2 CellInfo types;
+         */
+        CellInfoCdma cdma;
+    } ratSpecificInfo;
+};
+
 /** A union representing the CellIdentity of a single cell */
 safe_union CellIdentity {
     Monostate noinit;
@@ -553,3 +825,207 @@
     /** Control the unsolicited sending of barring info updates via onBarringInfo */
     BARRING_INFO = 1 << 6,
 };
+
+/**
+ * Call fail causes for Circuit-switched service enumerated in 3GPP TS 24.008, 10.5.3.6 and
+ * 10.5.147. Additional detail is available in 3GPP TS 24.008 Annex G.
+ */
+enum RegistrationFailCause : int32_t {
+    /** 0 - None */
+    NONE = 0,
+    /** 2 - IMSI unknown in HLR */
+    IMSI_UNKNOWN_IN_HLR = 2,
+    /** 3 - Illegal MS */
+    ILLEGAL_MS = 3,
+    /** 4 - Illegal ME */
+    IMSI_UNKNOWN_IN_VLR = 4,
+    /** 5 - PLMN not allowed */
+    IMEI_NOT_ACCEPTED = 5,
+    /** 6 - Location area not allowed */
+    ILLEGAL_ME = 6,
+    /** 7 - Roaming not allowed */
+    GPRS_SERVICES_NOT_ALLOWED = 7,
+    /** 8 - No Suitable Cells in this Location Area */
+    GPRS_AND_NON_GPRS_SERVICES_NOT_ALLOWED = 8,
+    /** 9 - Network failure */
+    MS_IDENTITY_CANNOT_BE_DERIVED_BY_NETWORK = 9,
+    /** 10 - Persistent location update reject */
+    IMPLICITLY_DETACHED = 10,
+    /** 11 - PLMN not allowed */
+    PLMN_NOT_ALLOWED = 11,
+    /** 12 - Location area not allowed */
+    LOCATION_AREA_NOT_ALLOWED = 12,
+    /** 13 - Roaming not allowed in this Location Area */
+    ROAMING_NOT_ALLOWED = 13,
+    /** 14 - GPRS Services not allowed in this PLMN */
+    GPRS_SERVICES_NOT_ALLOWED_IN_PLMN = 14,
+    /** 15 - No Suitable Cells in this Location Area */
+    NO_SUITABLE_CELLS = 15,
+    /** 16 - MSC temporarily not reachable */
+    MSC_TEMPORARILY_NOT_REACHABLE = 15,
+    /** 17 - Network Failure */
+    NETWORK_FAILURE = 17,
+    /** 20 - MAC Failure */
+    MAC_FAILURE = 20,
+    /** 21 - Sync Failure */
+    SYNC_FAILURE = 21,
+    /** 22 - Congestion */
+    CONGESTION = 22,
+    /** 23 - GSM Authentication unacceptable */
+    GSM_AUTHENTICATION_UNACCEPTABLE = 23,
+    /** 25 - Not Authorized for this CSG */
+    NOT_AUTHORIZED_FOR_THIS_CSG = 25,
+    /** 28 SMS provided via GPRS in this routing area */
+    SMS_PROVIDED_BY_GPRS_IN_ROUTING_AREA,
+    /** 32 - Service option not supported */
+    SERVICE_OPTION_NOT_SUPPORTED = 32,
+    /** 33 - Requested service option not subscribed */
+    SERVICE_OPTION_NOT_SUBSCRIBED = 33,
+    /** 34 - Service option temporarily out of order */
+    SERVICE_OPTION_TEMPORARILY_OUT_OF_ORDER = 34,
+    /** 38 - Call cannot be identified */
+    CALL_CANNOT_BE_IDENTIFIED = 38,
+    /** 40 No PDP context activated */
+    NO_PDP_CONTEXT_ACTIVATED = 40,
+    /** 48-63 - Retry upon entry into a new cell */
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_1 = 48,
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_2 = 49,
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_3 = 50,
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_4 = 51,
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_5 = 52,
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_6 = 53,
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_7 = 54,
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_8 = 55,
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_9 = 56,
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_10 = 57,
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_11 = 58,
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_12 = 59,
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_13 = 60,
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_14 = 61,
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_15 = 62,
+    RETRY_UPON_ENTRY_INTO_NEW_CELL_16 = 63,
+    /** 95 - Semantically incorrect message */
+    SEMANTICALLY_INCORRECT_MESSAGE = 95,
+    /** 96 - Invalid mandatory information */
+    INVALID_MANDATORY_INFORMATION = 96,
+    /** 97 - Message type non-existent or not implemented */
+    MESSAGE_TYPE_NON_EXISTENT_OR_NOT_IMPLEMENTED = 97,
+    /** 98 - Message type not compatible with protocol state */
+    MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 98,
+    /** 99 - Information element non-existent or not implemented */
+    INFORMATION_ELEMENT_NON_EXISTENT_OR_NOT_IMPLEMENTED = 99,
+    /** 100 - Conditional IE error */
+    CONDITIONAL_IE_ERROR = 100,
+    /** 101 - Message not compatible with protocol state */
+    MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101,
+    /** 111 - Protocol error, unspecified */
+    PROTOCOL_ERROR_UNSPECIFIED = 111,
+};
+
+enum PrlIndicator : int32_t {
+    NOT_REGISTERED = -1,
+    NOT_IN_PRL = 0,
+    IN_PRL = 1,
+};
+
+struct RegStateResult {
+    /**
+     * Registration state
+     *
+     * If the RAT is indicated as a GERAN, UTRAN, or CDMA2000 technology, this value reports
+     * registration in the Circuit-switched domain.
+     * If the RAT is indicated as an EUTRAN, NGRAN, or another technology that does not support
+     * circuit-switched services, this value reports registration in the Packet-switched domain.
+     */
+    RegState regState;
+
+    /**
+     * Indicates the available voice radio technology, valid values as
+     * defined by RadioTechnology.
+     */
+    RadioTechnology rat;
+
+    /**
+     * Cause code reported by the network in case registration fails. This will be a mobility
+     * management cause code defined for MM, GMM, MME or equivalent as appropriate for the RAT.
+     */
+    RegistrationFailCause reasonForDenial;
+
+    /** CellIdentity */
+    CellIdentity cellIdentity;
+
+    /**
+     * The most-recent PLMN-ID upon which the UE registered (or attempted to register if a failure
+     * is reported in the reasonForDenial field). This PLMN shall be in standard format consisting
+     * of a 3 digit MCC concatenated with a 2 or 3 digit MNC.
+     */
+    string registeredPlmn;
+
+    /**
+     * Access-technology-specific registration information, such as for Cdma2000.
+     */
+    safe_union AccessTechnologySpecificInfo {
+        Monostate noinit;
+
+        struct Cdma2000RegistrationInfo {
+            /**
+             * concurrent services support indicator. if registered on a CDMA system.
+             * false - Concurrent services not supported,
+             * true - Concurrent services supported
+             */
+            bool cssSupported;
+
+            /**
+             * TSB-58 Roaming Indicator if registered on a CDMA or EVDO system or -1 if not.
+             * Valid values are 0-255.
+             */
+            int32_t roamingIndicator;
+
+            /**
+             * Indicates whether the current system is in the PRL if registered on a CDMA or EVDO
+             * system or -1 if not. 0=not in the PRL, 1=in the PRL.
+             */
+            PrlIndicator systemIsInPrl;
+
+            /**
+             * Default Roaming Indicator from the PRL if registered on a CDMA or EVDO system or -1
+             * if not.
+             * Valid values are 0-255.
+             */
+            int32_t defaultRoamingIndicator;
+        } cdmaInfo;
+
+        struct EutranRegistrationInfo {
+            /**
+             * Network capabilities for voice over PS services. This info is valid only on LTE
+             * network and must be present when device is camped on LTE. vopsInfo must be empty when
+             * device is camped only on 2G/3G.
+             */
+            LteVopsInfo lteVopsInfo; // LTE network capability
+
+            /**
+             * The parameters of NR 5G Non-Standalone. This value is only valid on E-UTRAN,
+             * otherwise must be empty.
+             */
+            NrIndicators nrIndicators;
+        } eutranInfo;
+    } accessTechnologySpecificInfo;
+};
+
+/** Overwritten from @1.4::NetworkScanResult in order to update the CellInfo to 1.5 version. */
+struct NetworkScanResult {
+    /**
+     * The status of the scan.
+     */
+    ScanStatus status;
+
+    /**
+     * The error code of the incremental result.
+     */
+    RadioError error;
+
+    /**
+     * List of network information as CellInfo.
+     */
+    vec<CellInfo> networkInfos;
+};
diff --git a/radio/1.5/vts/functional/Android.bp b/radio/1.5/vts/functional/Android.bp
index 85c4f99..182985e 100644
--- a/radio/1.5/vts/functional/Android.bp
+++ b/radio/1.5/vts/functional/Android.bp
@@ -34,6 +34,7 @@
         "android.hardware.radio@1.0",
         "android.hardware.radio.config@1.0",
         "android.hardware.radio.config@1.1",
+        "android.hardware.radio.config@1.3",
     ],
     header_libs: ["radio.util.header@1.0"],
     test_suites: ["general-tests"]
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
index 77d9a02..09305de 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
@@ -833,23 +833,24 @@
 
     android::hardware::radio::V1_5::DataProfileInfo dataProfileInfo;
     memset(&dataProfileInfo, 0, sizeof(dataProfileInfo));
-    dataProfileInfo.base.profileId = DataProfileId::DEFAULT;
-    dataProfileInfo.base.apn = hidl_string("internet");
-    dataProfileInfo.base.protocol = PdpProtocolType::IP;
-    dataProfileInfo.base.roamingProtocol = PdpProtocolType::IP;
-    dataProfileInfo.base.authType = ApnAuthType::NO_PAP_NO_CHAP;
-    dataProfileInfo.base.user = hidl_string("username");
-    dataProfileInfo.base.password = hidl_string("password");
-    dataProfileInfo.base.type = DataProfileInfoType::THREE_GPP;
-    dataProfileInfo.base.maxConnsTime = 300;
-    dataProfileInfo.base.maxConns = 20;
-    dataProfileInfo.base.waitTime = 0;
-    dataProfileInfo.base.enabled = true;
+    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.base.bearerBitmap = 161543;
-    dataProfileInfo.base.mtu = 0;
-    dataProfileInfo.base.preferred = true;
-    dataProfileInfo.base.persistent = false;
+    dataProfileInfo.bearerBitmap = 161543;
+    dataProfileInfo.mtuV4 = 0;
+    dataProfileInfo.mtuV6 = 0;
+    dataProfileInfo.preferred = true;
+    dataProfileInfo.persistent = false;
 
     bool roamingAllowed = false;
 
@@ -878,29 +879,33 @@
     }
 }
 
+/*
+ * Test IRadio.setInitialAttachApn_1_5() for the response returned.
+ */
 TEST_F(RadioHidlTest_v1_5, setInitialAttachApn_1_5) {
     serial = GetRandomSerialNumber();
 
     // Create a dataProfileInfo
     android::hardware::radio::V1_5::DataProfileInfo dataProfileInfo;
     memset(&dataProfileInfo, 0, sizeof(dataProfileInfo));
-    dataProfileInfo.base.profileId = DataProfileId::DEFAULT;
-    dataProfileInfo.base.apn = hidl_string("internet");
-    dataProfileInfo.base.protocol = PdpProtocolType::IPV4V6;
-    dataProfileInfo.base.roamingProtocol = PdpProtocolType::IPV4V6;
-    dataProfileInfo.base.authType = ApnAuthType::NO_PAP_NO_CHAP;
-    dataProfileInfo.base.user = hidl_string("username");
-    dataProfileInfo.base.password = hidl_string("password");
-    dataProfileInfo.base.type = DataProfileInfoType::THREE_GPP;
-    dataProfileInfo.base.maxConnsTime = 300;
-    dataProfileInfo.base.maxConns = 20;
-    dataProfileInfo.base.waitTime = 0;
-    dataProfileInfo.base.enabled = true;
+    dataProfileInfo.profileId = DataProfileId::DEFAULT;
+    dataProfileInfo.apn = hidl_string("internet");
+    dataProfileInfo.protocol = PdpProtocolType::IPV4V6;
+    dataProfileInfo.roamingProtocol = PdpProtocolType::IPV4V6;
+    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.base.bearerBitmap = 161543;
-    dataProfileInfo.base.mtu = 0;
-    dataProfileInfo.base.preferred = true;
-    dataProfileInfo.base.persistent = false;
+    dataProfileInfo.bearerBitmap = 161543;
+    dataProfileInfo.mtuV4 = 0;
+    dataProfileInfo.mtuV6 = 0;
+    dataProfileInfo.preferred = true;
+    dataProfileInfo.persistent = false;
 
     radio_v1_5->setInitialAttachApn_1_5(serial, dataProfileInfo);
 
@@ -917,29 +922,33 @@
     }
 }
 
+/*
+ * Test IRadio.setDataProfile_1_5() for the response returned.
+ */
 TEST_F(RadioHidlTest_v1_5, setDataProfile_1_5) {
     serial = GetRandomSerialNumber();
 
     // Create a dataProfileInfo
     android::hardware::radio::V1_5::DataProfileInfo dataProfileInfo;
     memset(&dataProfileInfo, 0, sizeof(dataProfileInfo));
-    dataProfileInfo.base.profileId = DataProfileId::DEFAULT;
-    dataProfileInfo.base.apn = hidl_string("internet");
-    dataProfileInfo.base.protocol = PdpProtocolType::IPV4V6;
-    dataProfileInfo.base.roamingProtocol = PdpProtocolType::IPV4V6;
-    dataProfileInfo.base.authType = ApnAuthType::NO_PAP_NO_CHAP;
-    dataProfileInfo.base.user = hidl_string("username");
-    dataProfileInfo.base.password = hidl_string("password");
-    dataProfileInfo.base.type = DataProfileInfoType::THREE_GPP;
-    dataProfileInfo.base.maxConnsTime = 300;
-    dataProfileInfo.base.maxConns = 20;
-    dataProfileInfo.base.waitTime = 0;
-    dataProfileInfo.base.enabled = true;
+    dataProfileInfo.profileId = DataProfileId::DEFAULT;
+    dataProfileInfo.apn = hidl_string("internet");
+    dataProfileInfo.protocol = PdpProtocolType::IPV4V6;
+    dataProfileInfo.roamingProtocol = PdpProtocolType::IPV4V6;
+    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.base.bearerBitmap = 161543;
-    dataProfileInfo.base.mtu = 0;
-    dataProfileInfo.base.preferred = true;
-    dataProfileInfo.base.persistent = true;
+    dataProfileInfo.bearerBitmap = 161543;
+    dataProfileInfo.mtuV4 = 0;
+    dataProfileInfo.mtuV6 = 0;
+    dataProfileInfo.preferred = true;
+    dataProfileInfo.persistent = true;
 
     // Create a dataProfileInfoList
     android::hardware::hidl_vec<android::hardware::radio::V1_5::DataProfileInfo>
@@ -986,4 +995,74 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_5->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_5->rspInfo.serial);
     EXPECT_EQ(RadioError::NONE, radioRsp_v1_5->rspInfo.error);
-}
\ No newline at end of file
+}
+
+/*
+ * Test IRadio.setNetworkSelectionModeManual_1_5() for the response returned.
+ */
+TEST_F(RadioHidlTest_v1_5, setNetworkSelectionModeManual_1_5) {
+    serial = GetRandomSerialNumber();
+
+    // can't camp on nonexistent MCCMNC, so we expect this to fail.
+    Return<void> res = radio_v1_5->setNetworkSelectionModeManual_1_5(
+            serial, "123456", android::hardware::radio::V1_5::RadioAccessNetworks::GERAN);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_5->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_5->rspInfo.serial);
+
+    if (cardStatus.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error,
+                                     {RadioError::NONE, RadioError::ILLEGAL_SIM_OR_ME,
+                                      RadioError::INVALID_ARGUMENTS, RadioError::INVALID_STATE},
+                                     CHECK_GENERAL_ERROR));
+    } else if (cardStatus.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error,
+                                     {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE,
+                                      RadioError::INVALID_ARGUMENTS, RadioError::INVALID_STATE},
+                                     CHECK_GENERAL_ERROR));
+    }
+}
+
+/*
+ * Test IRadio.sendCdmaSmsExpectMore() for the response returned.
+ */
+TEST_F(RadioHidlTest_v1_5, sendCdmaSmsExpectMore) {
+    serial = GetRandomSerialNumber();
+
+    // Create a CdmaSmsAddress
+    CdmaSmsAddress cdmaSmsAddress;
+    cdmaSmsAddress.digitMode = CdmaSmsDigitMode::FOUR_BIT;
+    cdmaSmsAddress.numberMode = CdmaSmsNumberMode::NOT_DATA_NETWORK;
+    cdmaSmsAddress.numberType = CdmaSmsNumberType::UNKNOWN;
+    cdmaSmsAddress.numberPlan = CdmaSmsNumberPlan::UNKNOWN;
+    cdmaSmsAddress.digits = (std::vector<uint8_t>){11, 1, 6, 5, 10, 7, 7, 2, 10, 3, 10, 3};
+
+    // Create a CdmaSmsSubAddress
+    CdmaSmsSubaddress cdmaSmsSubaddress;
+    cdmaSmsSubaddress.subaddressType = CdmaSmsSubaddressType::NSAP;
+    cdmaSmsSubaddress.odd = false;
+    cdmaSmsSubaddress.digits = (std::vector<uint8_t>){};
+
+    // Create a CdmaSmsMessage
+    android::hardware::radio::V1_0::CdmaSmsMessage cdmaSmsMessage;
+    cdmaSmsMessage.teleserviceId = 4098;
+    cdmaSmsMessage.isServicePresent = false;
+    cdmaSmsMessage.serviceCategory = 0;
+    cdmaSmsMessage.address = cdmaSmsAddress;
+    cdmaSmsMessage.subAddress = cdmaSmsSubaddress;
+    cdmaSmsMessage.bearerData =
+            (std::vector<uint8_t>){15, 0, 3, 32, 3, 16, 1, 8, 16, 53, 76, 68, 6, 51, 106, 0};
+
+    radio_v1_5->sendCdmaSmsExpectMore(serial, cdmaSmsMessage);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_5->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_5->rspInfo.serial);
+
+    if (cardStatus.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_5->rspInfo.error,
+                {RadioError::INVALID_ARGUMENTS, RadioError::INVALID_STATE, RadioError::SIM_ABSENT},
+                CHECK_GENERAL_ERROR));
+    }
+}
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_test.cpp b/radio/1.5/vts/functional/radio_hidl_hal_test.cpp
index a5d236d..c29ebf9 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_test.cpp
+++ b/radio/1.5/vts/functional/radio_hidl_hal_test.cpp
@@ -47,9 +47,9 @@
     EXPECT_EQ(serial, radioRsp_v1_5->rspInfo.serial);
     EXPECT_EQ(RadioError::NONE, radioRsp_v1_5->rspInfo.error);
 
-    sp<::android::hardware::radio::config::V1_1::IRadioConfig> radioConfig =
+    sp<::android::hardware::radio::config::V1_3::IRadioConfig> radioConfig =
             ::testing::VtsHalHidlTargetTestBase::getService<
-                    ::android::hardware::radio::config::V1_1::IRadioConfig>();
+                    ::android::hardware::radio::config::V1_3::IRadioConfig>();
 
     /* Enforce Vts tesing with RadioConfig is existed. */
     ASSERT_NE(nullptr, radioConfig.get());
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
index 217caf5..6e6576e 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
+++ b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
@@ -22,7 +22,7 @@
 #include <condition_variable>
 #include <mutex>
 
-#include <android/hardware/radio/config/1.1/IRadioConfig.h>
+#include <android/hardware/radio/config/1.3/IRadioConfig.h>
 
 #include <android/hardware/radio/1.5/IRadio.h>
 #include <android/hardware/radio/1.5/IRadioIndication.h>
@@ -558,6 +558,24 @@
             const RadioResponseInfo& info,
             const ::android::hardware::hidl_vec<::android::hardware::radio::V1_5::BarringInfo>&
                     barringInfos);
+
+    Return<void> getVoiceRegistrationStateResponse_1_5(
+            const RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_5::RegStateResult& regResponse);
+
+    Return<void> getDataRegistrationStateResponse_1_5(
+            const RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_5::RegStateResult& regResponse);
+
+    Return<void> getCellInfoListResponse_1_5(
+            const RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_5::CellInfo>&
+                    cellInfo);
+
+    Return<void> setNetworkSelectionModeManualResponse_1_5(const RadioResponseInfo& info);
+
+    Return<void> sendCdmaSmsExpectMoreResponse(const RadioResponseInfo& info,
+                                               const SendSmsResult& sms);
 };
 
 /* Callback class for radio indication */
@@ -572,6 +590,15 @@
     /* 1.5 Api */
     Return<void> uiccApplicationsEnablementChanged(RadioIndicationType type, bool enabled);
 
+    Return<void> networkScanResult_1_5(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_5::NetworkScanResult& result);
+
+    Return<void> cellInfoList_1_5(
+            RadioIndicationType type,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_5::CellInfo>&
+                    records);
+
     /* 1.4 Api */
     Return<void> currentEmergencyNumberList(
             RadioIndicationType type,
diff --git a/radio/1.5/vts/functional/radio_indication.cpp b/radio/1.5/vts/functional/radio_indication.cpp
index 1483907..d448a22 100644
--- a/radio/1.5/vts/functional/radio_indication.cpp
+++ b/radio/1.5/vts/functional/radio_indication.cpp
@@ -350,3 +350,16 @@
         /*barringInfos*/) {
     return Void();
 }
+
+Return<void> RadioIndication_v1_5::networkScanResult_1_5(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_5::NetworkScanResult& /*result*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_5::cellInfoList_1_5(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_5::CellInfo>& /*records*/) {
+    return Void();
+}
diff --git a/radio/1.5/vts/functional/radio_response.cpp b/radio/1.5/vts/functional/radio_response.cpp
index 644a262..223acd0 100644
--- a/radio/1.5/vts/functional/radio_response.cpp
+++ b/radio/1.5/vts/functional/radio_response.cpp
@@ -969,3 +969,38 @@
     parent_v1_5.notify(info.serial);
     return Void();
 }
+
+Return<void> RadioResponse_v1_5::getVoiceRegistrationStateResponse_1_5(
+        const RadioResponseInfo& info,
+        const ::android::hardware::radio::V1_5::RegStateResult& /*regResponse*/) {
+    rspInfo = info;
+    parent_v1_5.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_5::getDataRegistrationStateResponse_1_5(
+        const RadioResponseInfo& info,
+        const ::android::hardware::radio::V1_5::RegStateResult& /*regResponse*/) {
+    rspInfo = info;
+    parent_v1_5.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_5::getCellInfoListResponse_1_5(
+        const RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_5::CellInfo>& /*cellInfo*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_5::setNetworkSelectionModeManualResponse_1_5(
+        const RadioResponseInfo& info) {
+    rspInfo = info;
+    parent_v1_5.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_5::sendCdmaSmsExpectMoreResponse(const RadioResponseInfo& /*info*/,
+                                                               const SendSmsResult& /*sms*/) {
+    return Void();
+}
diff --git a/radio/config/1.3/Android.bp b/radio/config/1.3/Android.bp
index 88de666..7360270 100644
--- a/radio/config/1.3/Android.bp
+++ b/radio/config/1.3/Android.bp
@@ -17,6 +17,9 @@
         "android.hardware.radio.config@1.1",
         "android.hardware.radio.config@1.2",
         "android.hardware.radio@1.0",
+        "android.hardware.radio@1.1",
+        "android.hardware.radio@1.4",
+        "android.hardware.radio@1.5",
         "android.hidl.base@1.0",
     ],
     gen_java: true,
diff --git a/radio/config/1.3/IRadioConfig.hal b/radio/config/1.3/IRadioConfig.hal
index a0ce6e0..d01f54b 100644
--- a/radio/config/1.3/IRadioConfig.hal
+++ b/radio/config/1.3/IRadioConfig.hal
@@ -27,5 +27,13 @@
  * serial to different methods), multiple responses (one for each method call) must still be served.
  */
 interface IRadioConfig extends @1.1::IRadioConfig {
-
+    /**
+     * Request current phone capability.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response callback is IRadioResponse.getPhoneCapabilityResponse_1_3() which
+     * will return <@1.3::PhoneCapability>.
+     */
+    oneway getPhoneCapability_1_3(int32_t serial);
 };
diff --git a/radio/config/1.3/IRadioConfigResponse.hal b/radio/config/1.3/IRadioConfigResponse.hal
index 9c4c971..e13aa1e 100644
--- a/radio/config/1.3/IRadioConfigResponse.hal
+++ b/radio/config/1.3/IRadioConfigResponse.hal
@@ -16,11 +16,22 @@
 
 package android.hardware.radio.config@1.3;
 
+import android.hardware.radio@1.0::RadioResponseInfo;
 import @1.2::IRadioConfigResponse;
+import @1.3::PhoneCapability;
 
 /**
  * Interface declaring response functions to solicited radio config requests.
  */
 interface IRadioConfigResponse extends @1.2::IRadioConfigResponse {
-
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param phoneCapability <@1.3::PhoneCapability> it defines modem's capability for example
+     *        how many logical modems it has, how many data connections it supports.
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     */
+    oneway getPhoneCapabilityResponse_1_3(RadioResponseInfo info, PhoneCapability phoneCapability);
 };
diff --git a/radio/config/1.3/default/RadioConfig.cpp b/radio/config/1.3/default/RadioConfig.cpp
index c28119c..01e98f1 100644
--- a/radio/config/1.3/default/RadioConfig.cpp
+++ b/radio/config/1.3/default/RadioConfig.cpp
@@ -24,7 +24,6 @@
 namespace implementation {
 
 using namespace ::android::hardware::radio::V1_0;
-using namespace ::android::hardware::radio::config;
 
 // Methods from ::android::hardware::radio::config::V1_0::IRadioConfig follow.
 Return<void> RadioConfig::setResponseFunctions(
@@ -105,6 +104,14 @@
     return Void();
 }
 
+// Methods from ::android::hardware::radio::config::V1_3::IRadioConfig follow.
+Return<void> RadioConfig::getPhoneCapability_1_3(int32_t /* serial */) {
+    V1_3::PhoneCapability phoneCapability;
+    RadioResponseInfo info;
+    mRadioConfigResponseV1_3->getPhoneCapabilityResponse_1_3(info, phoneCapability);
+    return Void();
+}
+
 }  // namespace implementation
 }  // namespace V1_3
 }  // namespace config
diff --git a/radio/config/1.3/default/RadioConfig.h b/radio/config/1.3/default/RadioConfig.h
index 00585e6..57ff368 100644
--- a/radio/config/1.3/default/RadioConfig.h
+++ b/radio/config/1.3/default/RadioConfig.h
@@ -62,6 +62,9 @@
     Return<void> setPreferredDataModem(int32_t serial, uint8_t modemId);
     Return<void> setModemsConfig(int32_t serial, const V1_1::ModemsConfig& modemsConfig);
     Return<void> getModemsConfig(int32_t serial);
+
+    // Methods from ::android::hardware::radio::config::V1_3::IRadioConfig follow.
+    Return<void> getPhoneCapability_1_3(int32_t serial);
 };
 
 }  // namespace implementation
diff --git a/radio/config/1.3/default/RadioConfigIndication.cpp b/radio/config/1.3/default/RadioConfigIndication.cpp
index eb77a48..608fa1c 100644
--- a/radio/config/1.3/default/RadioConfigIndication.cpp
+++ b/radio/config/1.3/default/RadioConfigIndication.cpp
@@ -23,10 +23,6 @@
 namespace V1_3 {
 namespace implementation {
 
-using namespace ::android::hardware::radio::V1_0;
-using namespace ::android::hardware::radio::config::V1_0;
-using namespace ::android::hardware::radio::config::V1_2;
-
 // Methods from ::android::hardware::radio::config::V1_0::IRadioConfigIndication follow.
 Return<void> RadioConfigIndication::simSlotsStatusChanged(
         RadioIndicationType /* type */, const hidl_vec<V1_0::SimSlotStatus>& /* slotStatus */) {
diff --git a/radio/config/1.3/default/RadioConfigIndication.h b/radio/config/1.3/default/RadioConfigIndication.h
index 3697492..c92446c 100644
--- a/radio/config/1.3/default/RadioConfigIndication.h
+++ b/radio/config/1.3/default/RadioConfigIndication.h
@@ -31,10 +31,6 @@
 using namespace ::android::hardware::radio::V1_0;
 using namespace ::android::hardware::radio::config;
 
-using ::android::sp;
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
diff --git a/radio/config/1.3/default/RadioConfigResponse.cpp b/radio/config/1.3/default/RadioConfigResponse.cpp
index 48e81da..1d48a13 100644
--- a/radio/config/1.3/default/RadioConfigResponse.cpp
+++ b/radio/config/1.3/default/RadioConfigResponse.cpp
@@ -23,11 +23,6 @@
 namespace V1_3 {
 namespace implementation {
 
-using namespace ::android::hardware::radio::V1_0;
-using namespace ::android::hardware::radio::config::V1_0;
-using namespace ::android::hardware::radio::config::V1_1;
-using namespace ::android::hardware::radio::config::V1_2;
-
 // Methods from ::android::hardware::radio::config::V1_0::IRadioConfigResponse follow.
 Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
         const RadioResponseInfo& /* info */,
@@ -73,6 +68,13 @@
     return Void();
 }
 
+// Methods from ::android::hardware::radio::config::V1_3::IRadioConfigResponse follow.
+Return<void> RadioConfigResponse::getPhoneCapabilityResponse_1_3(
+        const RadioResponseInfo& /* info */, const V1_3::PhoneCapability& /* phoneCapability */) {
+    // TODO implement
+    return Void();
+}
+
 }  // namespace implementation
 }  // namespace V1_3
 }  // namespace config
diff --git a/radio/config/1.3/default/RadioConfigResponse.h b/radio/config/1.3/default/RadioConfigResponse.h
index 0f0033f..dc169bb 100644
--- a/radio/config/1.3/default/RadioConfigResponse.h
+++ b/radio/config/1.3/default/RadioConfigResponse.h
@@ -28,43 +28,36 @@
 namespace V1_3 {
 namespace implementation {
 
-using ::android::sp;
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_string;
+using namespace ::android::hardware::radio::config;
+
 using ::android::hardware::hidl_vec;
 using ::android::hardware::Return;
-using ::android::hardware::Void;
+
+using ::android::hardware::radio::V1_0::RadioResponseInfo;
 
 struct RadioConfigResponse : public IRadioConfigResponse {
     // Methods from ::android::hardware::radio::config::V1_0::IRadioConfigResponse follow.
     Return<void> getSimSlotsStatusResponse(
-            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
-            const hidl_vec<::android::hardware::radio::config::V1_0::SimSlotStatus>& slotStatus)
-            override;
-    Return<void> setSimSlotsMappingResponse(
-            const ::android::hardware::radio::V1_0::RadioResponseInfo& info) override;
+            const RadioResponseInfo& info,
+            const hidl_vec<V1_0::SimSlotStatus>& slotStatus) override;
+    Return<void> setSimSlotsMappingResponse(const RadioResponseInfo& info) override;
 
     // Methods from ::android::hardware::radio::config::V1_1::IRadioConfigResponse follow.
-    Return<void> getPhoneCapabilityResponse(
-            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
-            const ::android::hardware::radio::config::V1_1::PhoneCapability& phoneCapability)
-            override;
-    Return<void> setPreferredDataModemResponse(
-            const ::android::hardware::radio::V1_0::RadioResponseInfo& info) override;
-    Return<void> setModemsConfigResponse(
-            const ::android::hardware::radio::V1_0::RadioResponseInfo& info) override;
-    Return<void> getModemsConfigResponse(
-            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
-            const ::android::hardware::radio::config::V1_1::ModemsConfig& modemsConfig) override;
+    Return<void> getPhoneCapabilityResponse(const RadioResponseInfo& info,
+                                            const V1_1::PhoneCapability& phoneCapability) override;
+    Return<void> setPreferredDataModemResponse(const RadioResponseInfo& info) override;
+    Return<void> setModemsConfigResponse(const RadioResponseInfo& info) override;
+    Return<void> getModemsConfigResponse(const RadioResponseInfo& info,
+                                         const V1_1::ModemsConfig& modemsConfig) override;
 
     // Methods from ::android::hardware::radio::config::V1_2::IRadioConfigResponse follow.
     Return<void> getSimSlotsStatusResponse_1_2(
-            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
-            const hidl_vec<::android::hardware::radio::config::V1_2::SimSlotStatus>& slotStatus)
-            override;
+            const RadioResponseInfo& info,
+            const hidl_vec<V1_2::SimSlotStatus>& slotStatus) override;
 
-    // Methods from ::android::hidl::base::V1_0::IBase follow.
+    // Methods from ::android::hardware::radio::config::V1_3::IRadioConfigResponse follow.
+    Return<void> getPhoneCapabilityResponse_1_3(
+            const RadioResponseInfo& info, const V1_3::PhoneCapability& phoneCapability) override;
 };
 
 }  // namespace implementation
diff --git a/radio/config/1.3/types.hal b/radio/config/1.3/types.hal
index 866002a..7860006 100644
--- a/radio/config/1.3/types.hal
+++ b/radio/config/1.3/types.hal
@@ -15,3 +15,181 @@
  */
 
 package android.hardware.radio.config@1.3;
+
+import android.hardware.radio@1.1::GeranBands;
+import android.hardware.radio@1.1::EutranBands;
+import android.hardware.radio@1.4::RadioAccessFamily;
+import android.hardware.radio@1.5::NgranBands;
+import android.hardware.radio@1.5::UtranBands;
+
+/** Type for the SIM slot. */
+enum SlotType : int32_t {
+    /** Slot type for UICC/pSIM (physical SIM). */
+    UICC       = 1,
+    /** Slot type for iUICC/iSIM (integrated SIM). */
+    IUICC      = 2,
+    /** Slot type for eUICC/eSIM (embedded SIM). */
+    EUICC      = 3,
+    /** Slot type for soft SIM (no physical SIM). */
+    SOFT_SIM   = 4,
+};
+
+/** A field in PhoneCapability that holds information about the SIM slot. */
+struct SimSlotCapability {
+    /** Corresponds to physicalSlotId in Radio@1.2::CardStatus. */
+    uint32_t physicalSlotId;
+
+    /** Type of slot. */
+    SlotType slotType;
+};
+
+/** Bitmask of features that can be supported by a single modem. */
+enum ModemFeatures : int32_t {
+    /** 3GPP2 capability. */
+    THREE_GPP2_REG            = 1 << 0,
+    /** 3GPP capability. */
+    THREE_GPP_REG             = 1 << 1,
+    /** CDMA 2000 with EHRPD capability. */
+    CDMA2000_EHRPD_REG        = 1 << 2,
+    /** GSM capability. */
+    GERAN_REG                 = 1 << 3,
+    /** UMTS capability. */
+    UTRAN_REG                 = 1 << 4,
+    /** LTE capability. */
+    EUTRAN_REG                = 1 << 5,
+    /** 5G capability. */
+    NGRAN_REG                 = 1 << 6,
+    /** Dual Connectivity capability. */
+    EN_DC_REG                 = 1 << 7,
+    /** VoLTE capability (IMS registered). */
+    PS_VOICE_REG              = 1 << 8,
+    /** CS voice call capability. */
+    CS_VOICE_SESSION          = 1 << 9,
+    /** Internet connection capability. */
+    INTERACTIVE_DATA_SESSION  = 1 << 10,
+    /** Dedicated bearer capability. */
+    DEDICATED_BEARER          = 1 << 11,
+    /** Network scanning capability. */
+    NETWORK_SCAN              = 1 << 12,
+    /** CDMA capability for SIM associated with modem. */
+    CSIM                      = 1 << 13,
+};
+
+struct ConcurrentModemFeatures {
+    /**
+     * A vector of concurrently supportable modem features across all modems.
+     * Each entry in the vector is a bitfield of ModemFeatures that can be used
+     * concurrently with the other ModemFeatures in that list.
+     * Each bitfield must be the full set of features for a single modem.
+     *
+     * On a Dual-SIM device, each entry will be a vector of length 2.
+     * The examples below depict the modemFeatures for four Dual-SIM setups:
+     * 1. Only one modem can PS attach (IMS registered).
+     *    {
+     *        (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
+     *            CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
+     *        (GERAN_REG | UTRAN_REG)
+     *    }
+     *    or
+     *    {
+     *        (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
+     *            INTERACTIVE_DATA_SESSION),
+     *        (GERAN_REG | UTRAN_REG | CS_VOICE_SESSION)
+     *    }
+     * 2. Both modems can PS attach (dual VoLTE).
+     *    {
+     *        (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
+     *            CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
+     *        (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG)
+     *    }
+     * 3. Both modems can maintain an Internet connection, but they share
+     *    one RF hardware.
+     *    {
+     *        (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
+     *            CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
+     *        (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
+     *            INTERACTIVE_DATA_SESSION)
+     *    }
+     * 4. Both modems can maintain an Internet connection, and they have
+     *    their own RF hardware.
+     *    {
+     *        (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
+     *            CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
+     *        (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
+     *            INTERACTIVE_DATA_SESSION | DEDICATED_BEARER)
+     *    }
+     */
+    vec<bitfield<ModemFeatures>> modemFeatures;
+};
+
+/**
+ * Overwritten from @1.1::PhoneCapability to add new capabilities and deprecate
+ * maxActiveData, maxActiveInternetData, isInternetLingeringSupported, logicalModemList.
+ * Replaces RadioConfig@1.1::ModemInfo and should replace Radio@1.4::RadioCapabilities
+ * in the next major version upgrade. In the future, this should be extended instead of overwritten.
+ */
+struct PhoneCapability {
+    /**
+     * 3GPP UE category for UTRAN downlink direction.
+     * 25.306 Table 5.1a
+     */
+    uint8_t utranUeCategoryDl;
+    /**
+     * 3GPP UE category for UTRAN uplink direction.
+     * 25.306 Table 5.1g
+     */
+    uint8_t utranUeCategoryUl;
+    /**
+     * 3GPP UE category for EUTRAN downlink direction.
+     * 25.306 Table 4.1a
+     */
+    uint8_t eutranUeCategoryDl;
+    /**
+     * 3GPP UE category for EUTRAN uplink direction.
+     * 25.306 Table 4.1a-2
+     */
+    uint8_t eutranUeCategoryUl;
+
+    /**
+     * Length of grace period for switching between logical modems, in milliseconds.
+     * Used only when the number of logical modems is greater than the number of
+     * Internet connections the device can support, otherwise must be 0.
+     */
+    uint64_t psDataConnectionLingerTimeMillis;
+
+    vec<GeranBands> geranBands;
+    vec<UtranBands> utranBands;
+    vec<EutranBands> eutranBands;
+    vec<NgranBands> ngranBands;
+
+    /**
+     * 32-bit bitmap of supported Radio@1.4::RadioAccessFamily types.
+     * Note that RadioAccessFamily is actually the radio access technologies, so it should be
+     * renamed in the next major version upgrade.
+     */
+    bitfield<RadioAccessFamily> supportedRats;
+
+    /**
+     * List of unique logical modem UUIDs from Radio@1.4::RadioCapabilities.
+     * A UUID is typically "com.xxxx.lmX" where X is the logical modem ID.
+     * Must be equal to the number of logical modems in the device.
+     * Radio@1.2::RadioConst::MAX_UUID_LENGTH is the max length of each UUID.
+     */
+    vec<string> logicalModemUuids;
+
+    /**
+     * List of SIM slot capabilities. The order of physical slot IDs must correspond to
+     * the order of modems in logicalModemUuids.
+     */
+    vec<SimSlotCapability> simSlotCapabilities;
+
+    /**
+     * A vector of all sets of concurrently supportable modem feature sets. The order of modems
+     * in modemFeatures must correspond to the order of modems in logicalModemUuids.
+     * Each entry in concurrentFeatureSupport is independent of others in the list
+     * and represents a set of concurrently supportable features across all modems.
+     * Each entry in ConcurrentModemFeatures::modemFeatures is a bitfield of
+     * concurrently supported ModemFeatures for one modem.
+     */
+    vec<ConcurrentModemFeatures> concurrentFeatureSupport;
+};
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp b/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
index 07e9ede..7f90023 100644
--- a/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
@@ -17,3 +17,34 @@
 #include <radio_config_hidl_hal_utils.h>
 
 #define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+
+/*
+ * Test IRadioConfig.getPhoneCapability_1_3()
+ */
+TEST_P(RadioConfigHidlTest, getPhoneCapability_1_3) {
+    serial = GetRandomSerialNumber();
+    Return<void> res = radioConfig->getPhoneCapability_1_3(serial);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioConfigRsp->rspInfo.type);
+    EXPECT_EQ(serial, radioConfigRsp->rspInfo.serial);
+    ALOGI("getPhoneCapability_1_3, rspInfo.error = %s\n",
+          toString(radioConfigRsp->rspInfo.error).c_str());
+
+    ASSERT_TRUE(CheckAnyOfErrors(
+            radioConfigRsp->rspInfo.error,
+            {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::INTERNAL_ERR}));
+
+    if (radioConfigRsp->rspInfo.error == RadioError ::NONE) {
+        int numModems = radioConfigRsp->phoneCap_1_3.logicalModemUuids.size();
+        EXPECT_GE(numModems, 0);
+        // length of simSlotCapabilities should be equal to length of logicalModemUuids.
+        EXPECT_EQ(numModems, radioConfigRsp->phoneCap_1_3.simSlotCapabilities.size());
+        // length of modemFeatures in each ConcurrentModemFeatures should be
+        // equal to length of logicalModemUuids.
+        for (V1_3::ConcurrentModemFeatures cmf :
+             radioConfigRsp->phoneCap_1_3.concurrentFeatureSupport) {
+            EXPECT_EQ(numModems, cmf.modemFeatures.size());
+        }
+    }
+}
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 dbb4bf4..cd48b25 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
@@ -17,7 +17,7 @@
 #include <radio_config_hidl_hal_utils.h>
 
 void RadioConfigHidlTest::SetUp() {
-    radioConfig = ::android::hardware::radio::config::V1_3::IRadioConfig::getService(GetParam());
+    radioConfig = V1_3::IRadioConfig::getService(GetParam());
     ASSERT_NE(nullptr, radioConfig.get());
 
     radioConfigRsp = new (std::nothrow) RadioConfigResponse(*this);
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 9b78c04..b21c7c0 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
@@ -28,19 +28,18 @@
 #include <gtest/gtest.h>
 #include <hidl/GtestPrinter.h>
 #include <hidl/ServiceManagement.h>
+#include <log/log.h>
 
 #include "vts_test_util.h"
 
-using namespace ::android::hardware::radio::config::V1_1;
-using namespace ::android::hardware::radio::config::V1_2;
-using namespace ::android::hardware::radio::config::V1_3;
+using namespace ::android::hardware::radio::config;
 
 using ::android::sp;
-using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 
+using ::android::hardware::radio::V1_0::RadioIndicationType;
 using ::android::hardware::radio::V1_0::RadioResponseInfo;
 using ::android::hardware::radio::V1_0::RadioResponseType;
 
@@ -49,43 +48,46 @@
 class RadioConfigHidlTest;
 
 /* Callback class for radio config response */
-class RadioConfigResponse : public ::android::hardware::radio::config::V1_3::IRadioConfigResponse {
+class RadioConfigResponse : public V1_3::IRadioConfigResponse {
   protected:
     RadioConfigHidlTest& parent;
 
   public:
     RadioResponseInfo rspInfo;
-    PhoneCapability phoneCap;
+    V1_1::PhoneCapability phoneCap_1_1;
+    V1_3::PhoneCapability phoneCap_1_3;
 
     RadioConfigResponse(RadioConfigHidlTest& parent);
     virtual ~RadioConfigResponse() = default;
 
     /* 1.0 Api */
-    Return<void> getSimSlotsStatusResponse(
-            const RadioResponseInfo& info,
-            const hidl_vec<::android::hardware::radio::config::V1_0::SimSlotStatus>& slotStatus);
+    Return<void> getSimSlotsStatusResponse(const RadioResponseInfo& info,
+                                           const hidl_vec<V1_0::SimSlotStatus>& slotStatus);
 
     Return<void> setSimSlotsMappingResponse(const RadioResponseInfo& info);
 
     /* 1.1 Api */
     Return<void> getPhoneCapabilityResponse(const RadioResponseInfo& info,
-                                            const PhoneCapability& phoneCapability);
+                                            const V1_1::PhoneCapability& phoneCapability);
 
     Return<void> setPreferredDataModemResponse(const RadioResponseInfo& info);
 
     Return<void> getModemsConfigResponse(const RadioResponseInfo& info,
-                                         const ModemsConfig& mConfig);
+                                         const V1_1::ModemsConfig& mConfig);
 
     Return<void> setModemsConfigResponse(const RadioResponseInfo& info);
 
     /* 1.2 Api */
     Return<void> getSimSlotsStatusResponse_1_2(const RadioResponseInfo& info,
-                                               const hidl_vec<SimSlotStatus>& slotStatus);
+                                               const hidl_vec<V1_2::SimSlotStatus>& slotStatus);
+
+    /* 1.3 Api */
+    Return<void> getPhoneCapabilityResponse_1_3(const RadioResponseInfo& info,
+                                                const V1_3::PhoneCapability& phoneCapability);
 };
 
 /* Callback class for radio config indication */
-class RadioConfigIndication
-    : public ::android::hardware::radio::config::V1_3::IRadioConfigIndication {
+class RadioConfigIndication : public V1_3::IRadioConfigIndication {
   protected:
     RadioConfigHidlTest& parent;
 
@@ -94,9 +96,8 @@
     virtual ~RadioConfigIndication() = default;
 
     /* 1.2 Api */
-    Return<void> simSlotsStatusChanged_1_2(
-            ::android::hardware::radio::V1_0::RadioIndicationType type,
-            const hidl_vec<SimSlotStatus>& slotStatus);
+    Return<void> simSlotsStatusChanged_1_2(RadioIndicationType type,
+                                           const hidl_vec<V1_2::SimSlotStatus>& slotStatus);
 };
 
 // The main test class for Radio config HIDL.
@@ -121,7 +122,7 @@
     int serial;
 
     /* radio config service handle */
-    sp<::android::hardware::radio::config::V1_3::IRadioConfig> radioConfig;
+    sp<V1_3::IRadioConfig> radioConfig;
 
     /* radio config response handle */
     sp<RadioConfigResponse> radioConfigRsp;
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 1ca960e..22098d3 100644
--- a/radio/config/1.3/vts/functional/radio_config_response.cpp
+++ b/radio/config/1.3/vts/functional/radio_config_response.cpp
@@ -16,17 +16,18 @@
 
 #include <radio_config_hidl_hal_utils.h>
 
-using ::android::hardware::radio::V1_0::RadioResponseInfo;
+using namespace ::android::hardware::radio::config;
 
-// SimSlotStatus slotStatus;
+using ::android::hardware::hidl_vec;
+
+using ::android::hardware::radio::V1_0::RadioResponseInfo;
 
 RadioConfigResponse::RadioConfigResponse(RadioConfigHidlTest& parent) : parent(parent) {}
 
 /* 1.0 Apis */
 Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
         const RadioResponseInfo& /* info */,
-        const ::android::hardware::hidl_vec<
-                ::android::hardware::radio::config::V1_0::SimSlotStatus>& /* slotStatus */) {
+        const hidl_vec<V1_0::SimSlotStatus>& /* slotStatus */) {
     return Void();
 }
 
@@ -36,9 +37,9 @@
 
 /* 1.1 Apis */
 Return<void> RadioConfigResponse::getPhoneCapabilityResponse(
-        const RadioResponseInfo& info, const PhoneCapability& phoneCapability) {
+        const RadioResponseInfo& info, const V1_1::PhoneCapability& phoneCapability) {
     rspInfo = info;
-    phoneCap = phoneCapability;
+    phoneCap_1_1 = phoneCapability;
     parent.notify(info.serial);
     return Void();
 }
@@ -49,7 +50,7 @@
 }
 
 Return<void> RadioConfigResponse::getModemsConfigResponse(const RadioResponseInfo& /* info */,
-                                                          const ModemsConfig& /* mConfig */) {
+                                                          const V1_1::ModemsConfig& /* mConfig */) {
     return Void();
 }
 
@@ -60,6 +61,15 @@
 /* 1.2 Apis */
 Return<void> RadioConfigResponse::getSimSlotsStatusResponse_1_2(
         const RadioResponseInfo& /* info */,
-        const ::android::hardware::hidl_vec<SimSlotStatus>& /* slotStatus */) {
+        const hidl_vec<V1_2::SimSlotStatus>& /* slotStatus */) {
     return Void();
-}
\ No newline at end of file
+}
+
+/* 1.3 Apis */
+Return<void> RadioConfigResponse::getPhoneCapabilityResponse_1_3(
+        const RadioResponseInfo& info, const V1_3::PhoneCapability& phoneCapability) {
+    rspInfo = info;
+    phoneCap_1_3 = phoneCapability;
+    parent.notify(info.serial);
+    return Void();
+}
diff --git a/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp b/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp
index f69cf87..cd8cc3e 100644
--- a/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp
+++ b/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp
@@ -26,6 +26,9 @@
 using android::String16;
 using android::hardware::rebootescrow::IRebootEscrow;
 
+#define SKIP_UNSUPPORTED \
+    if (rebootescrow == nullptr) GTEST_SKIP() << "Not supported on this device"
+
 /**
  * This tests that the key can be written, read, and removed. It does not test
  * that the key survives a reboot. That needs a host-based test.
@@ -36,7 +39,6 @@
   public:
     virtual void SetUp() override {
         rebootescrow = android::waitForDeclaredService<IRebootEscrow>(String16(GetParam().c_str()));
-        ASSERT_NE(rebootescrow, nullptr);
     }
 
     sp<IRebootEscrow> rebootescrow;
@@ -59,6 +61,8 @@
 };
 
 TEST_P(RebootEscrowAidlTest, StoreAndRetrieve_Success) {
+    SKIP_UNSUPPORTED;
+
     ASSERT_TRUE(rebootescrow->storeKey(KEY_1).isOk());
 
     std::vector<uint8_t> actualKey;
@@ -67,6 +71,8 @@
 }
 
 TEST_P(RebootEscrowAidlTest, StoreAndRetrieve_SecondRetrieveSucceeds) {
+    SKIP_UNSUPPORTED;
+
     ASSERT_TRUE(rebootescrow->storeKey(KEY_1).isOk());
 
     std::vector<uint8_t> actualKey;
@@ -78,6 +84,8 @@
 }
 
 TEST_P(RebootEscrowAidlTest, StoreTwiceOverwrites_Success) {
+    SKIP_UNSUPPORTED;
+
     ASSERT_TRUE(rebootescrow->storeKey(KEY_1).isOk());
     ASSERT_TRUE(rebootescrow->storeKey(KEY_2).isOk());
 
@@ -87,6 +95,8 @@
 }
 
 TEST_P(RebootEscrowAidlTest, StoreEmpty_AfterGetEmptyKey_Success) {
+    SKIP_UNSUPPORTED;
+
     rebootescrow->storeKey(KEY_1);
     rebootescrow->storeKey(EMPTY_KEY);
 
diff --git a/sensors/2.0/multihal/HalProxy.cpp b/sensors/2.0/multihal/HalProxy.cpp
index 03ff605..fd76bda 100644
--- a/sensors/2.0/multihal/HalProxy.cpp
+++ b/sensors/2.0/multihal/HalProxy.cpp
@@ -486,15 +486,14 @@
                 }
             }
             lock.lock();
+            mSizePendingWriteEventsQueue -= numToWrite;
             if (pendingWriteEvents.size() > eventQueueSize) {
                 // TODO(b/143302327): Check if this erase operation is too inefficient. It will copy
                 // all the events ahead of it down to fill gap off array at front after the erase.
                 pendingWriteEvents.erase(pendingWriteEvents.begin(),
                                          pendingWriteEvents.begin() + eventQueueSize);
-                mSizePendingWriteEventsQueue -= eventQueueSize;
             } else {
                 mPendingWriteEventsQueue.pop();
-                mSizePendingWriteEventsQueue -= pendingWriteEvents.size();
             }
         }
     }
diff --git a/tv/tuner/1.0/TEST_MAPPING b/tv/tuner/1.0/TEST_MAPPING
new file mode 100644
index 0000000..1979887
--- /dev/null
+++ b/tv/tuner/1.0/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "VtsHalTvTunerV1_0TargetTest"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tv/tuner/1.0/types.hal b/tv/tuner/1.0/types.hal
index e22e487..2eaab36 100644
--- a/tv/tuner/1.0/types.hal
+++ b/tv/tuner/1.0/types.hal
@@ -785,6 +785,7 @@
 /**
  *   Physical Layer Pipe (PLP) Mode for DVBT.
  */
+@export
 enum FrontendDvbtPlpMode : uint32_t {
     UNDEFINED,
     AUTO,
@@ -1130,9 +1131,14 @@
 @export
 enum FrontendAnalogType : uint32_t {
     UNDEFINED = 0,
-    PAL = 1 << 0,
-    SECAM = 1 << 1,
-    NTSC = 1 << 2,
+    AUTO = 1 << 0,
+    PAL = 1 << 1,
+    PAL_M = 1 << 2,
+    PAL_N = 1 << 3,
+    PAL_60 = 1 << 4,
+    NTSC = 1 << 5,
+    NTSC_443 = 1 << 6,
+    SECAM = 1 << 7,
 };
 
 /**
@@ -1141,23 +1147,24 @@
 @export
 enum FrontendAnalogSifStandard : uint32_t {
     UNDEFINED = 0,
-    BG = 1 << 0,
-    BG_A2 = 1 << 1,
-    BG_NICAM = 1 << 2,
-    I = 1 << 3,
-    DK = 1 << 4,
-    DK1 = 1 << 5,
-    DK2 = 1 << 6,
-    DK3 = 1 << 7,
-    DK_NICAM = 1 << 8,
-    L = 1 << 9,
-    M = 1 << 10,
-    M_BTSC = 1 << 11,
-    M_A2 = 1 << 12,
-    M_EIA_J = 1 << 13,
-    I_NICAM = 1 << 14,
-    L_NICAM = 1 << 15,
-    L_PRIME = 1 << 16,
+    AUTO = 1 << 0,
+    BG = 1 << 1,
+    BG_A2 = 1 << 2,
+    BG_NICAM = 1 << 3,
+    I = 1 << 4,
+    DK = 1 << 5,
+    DK1_A2 = 1 << 6,
+    DK2_A2 = 1 << 7,
+    DK3_A2 = 1 << 8,
+    DK_NICAM = 1 << 9,
+    L = 1 << 10,
+    M = 1 << 11,
+    M_BTSC = 1 << 12,
+    M_A2 = 1 << 13,
+    M_EIAJ = 1 << 14,
+    I_NICAM = 1 << 15,
+    L_NICAM = 1 << 16,
+    L_PRIME = 1 << 17,
 };
 
 /**
diff --git a/tv/tuner/1.0/vts/functional/Android.bp b/tv/tuner/1.0/vts/functional/Android.bp
index 7d6b990..3637708 100644
--- a/tv/tuner/1.0/vts/functional/Android.bp
+++ b/tv/tuner/1.0/vts/functional/Android.bp
@@ -30,5 +30,10 @@
     shared_libs: [
         "libbinder",
     ],
-    test_suites: ["general-tests"],
+    test_suites: [
+        "general-tests",
+        "vts-core",
+    ],
+
+    require_root: true,
 }
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index 7977f25..820c58c 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -31,8 +31,11 @@
 #include <android/hardware/tv/tuner/1.0/types.h>
 #include <binder/MemoryDealer.h>
 #include <fmq/MessageQueue.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
 #include <hidl/HidlSupport.h>
 #include <hidl/HidlTransportSupport.h>
+#include <hidl/ServiceManagement.h>
 #include <hidl/Status.h>
 #include <hidlmemory/FrameworkUtils.h>
 #include <utils/Condition.h>
@@ -633,23 +636,10 @@
     android::Mutex::Autolock autoLock(mRecordThreadLock);
 }
 
-// Test environment for Tuner HIDL HAL.
-class TunerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
-  public:
-    // get the test environment singleton
-    static TunerHidlEnvironment* Instance() {
-        static TunerHidlEnvironment* instance = new TunerHidlEnvironment;
-        return instance;
-    }
-
-    virtual void registerTestServices() override { registerTestService<ITuner>(); }
-};
-
-class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+class TunerHidlTest : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
-        mService = ::testing::VtsHalHidlTargetTestBase::getService<ITuner>(
-                TunerHidlEnvironment::Instance()->getServiceName<ITuner>());
+        mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
     }
 
@@ -1242,7 +1232,7 @@
 /*
  * API STATUS TESTS
  */
-TEST_F(TunerHidlTest, CreateFrontend) {
+TEST_P(TunerHidlTest, CreateFrontend) {
     Result status;
     hidl_vec<FrontendId> feIds;
 
@@ -1262,7 +1252,7 @@
     }
 }
 
-TEST_F(TunerHidlTest, TuneFrontend) {
+TEST_P(TunerHidlTest, TuneFrontend) {
     Result status;
     hidl_vec<FrontendId> feIds;
 
@@ -1282,7 +1272,7 @@
     }
 }
 
-TEST_F(TunerHidlTest, StopTuneFrontend) {
+TEST_P(TunerHidlTest, StopTuneFrontend) {
     Result status;
     hidl_vec<FrontendId> feIds;
 
@@ -1302,7 +1292,7 @@
     }
 }
 
-TEST_F(TunerHidlTest, CloseFrontend) {
+TEST_P(TunerHidlTest, CloseFrontend) {
     Result status;
     hidl_vec<FrontendId> feIds;
 
@@ -1322,7 +1312,7 @@
     }
 }
 
-/*TEST_F(TunerHidlTest, CreateDemuxWithFrontend) {
+TEST_P(TunerHidlTest, CreateDemuxWithFrontend) {
     Result status;
     hidl_vec<FrontendId> feIds;
 
@@ -1347,32 +1337,34 @@
         ASSERT_TRUE(createDemuxWithFrontend(feIds[i], settings));
         mFrontend->stopTune();
     }
-}*/
+}
 
-TEST_F(TunerHidlTest, CreateDemux) {
+TEST_P(TunerHidlTest, CreateDemux) {
     description("Create Demux");
     ASSERT_TRUE(createDemux());
 }
 
-TEST_F(TunerHidlTest, CloseDemux) {
+TEST_P(TunerHidlTest, CloseDemux) {
     description("Close Demux");
     ASSERT_TRUE(closeDemux());
 }
 
-TEST_F(TunerHidlTest, CreateDescrambler) {
+TEST_P(TunerHidlTest, CreateDescrambler) {
     description("Create Descrambler");
     ASSERT_TRUE(createDescrambler());
 }
 
-TEST_F(TunerHidlTest, CloseDescrambler) {
+TEST_P(TunerHidlTest, CloseDescrambler) {
     description("Close Descrambler");
     ASSERT_TRUE(closeDescrambler());
 }
 
 /*
  * DATA FLOW TESTS
+ *
+ * TODO: re-enable the tests after finalizing the testing stream.
  */
-TEST_F(TunerHidlTest, PlaybackDataFlowWithSectionFilterTest) {
+/*TEST_P(TunerHidlTest, PlaybackDataFlowWithSectionFilterTest) {
     description("Feed ts data from playback and configure pes filter to get output");
 
     // todo modulize the filter conf parser
@@ -1415,7 +1407,7 @@
     ASSERT_TRUE(playbackDataFlowTest(filterConf, playbackConf, goldenOutputFiles));
 }
 
-TEST_F(TunerHidlTest, BroadcastDataFlowWithPesFilterTest) {
+TEST_P(TunerHidlTest, BroadcastDataFlowWithPesFilterTest) {
     description("Feed ts data from frontend and test with PES filter");
 
     // todo modulize the filter conf parser
@@ -1445,7 +1437,7 @@
     ASSERT_TRUE(broadcastDataFlowTest(filterConf, goldenOutputFiles));
 }
 
-TEST_F(TunerHidlTest, RecordDataFlowWithTsRecordFilterTest) {
+TEST_P(TunerHidlTest, RecordDataFlowWithTsRecordFilterTest) {
     description("Feed ts data from frontend to recording and test with ts record filter");
 
     // todo modulize the filter conf parser
@@ -1481,15 +1473,11 @@
     vector<string> goldenOutputFiles;
 
     ASSERT_TRUE(recordDataFlowTest(filterConf, recordSetting, goldenOutputFiles));
-}
+}*/
 
 }  // namespace
 
-int main(int argc, char** argv) {
-    ::testing::AddGlobalTestEnvironment(TunerHidlEnvironment::Instance());
-    ::testing::InitGoogleTest(&argc, argv);
-    TunerHidlEnvironment::Instance()->init(&argc, argv);
-    int status = RUN_ALL_TESTS();
-    LOG(INFO) << "Test result = " << status;
-    return status;
-}
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, TunerHidlTest,
+        testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
+        android::hardware::PrintInstanceNameToString);
diff --git a/usb/gadget/1.1/Android.bp b/usb/gadget/1.1/Android.bp
new file mode 100644
index 0000000..b41eb9c
--- /dev/null
+++ b/usb/gadget/1.1/Android.bp
@@ -0,0 +1,17 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.usb.gadget@1.1",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "IUsbGadget.hal",
+    ],
+    interfaces: [
+        "android.hardware.usb.gadget@1.0",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+}
diff --git a/usb/gadget/1.1/IUsbGadget.hal b/usb/gadget/1.1/IUsbGadget.hal
new file mode 100644
index 0000000..af88ef0
--- /dev/null
+++ b/usb/gadget/1.1/IUsbGadget.hal
@@ -0,0 +1,31 @@
+/*
+ * 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.usb.gadget@1.1;
+
+import @1.0::IUsbGadget;
+import @1.0::Status;
+
+interface IUsbGadget extends @1.0::IUsbGadget {
+    /**
+     * This function is used to reset USB gadget driver.
+     * Performs USB data connection reset. The connection will disconnect and
+     * reconnect.
+     *
+     * return status indicate success or not.
+     */
+    reset() generates(Status status);
+};
diff --git a/usb/gadget/1.1/default/Android.bp b/usb/gadget/1.1/default/Android.bp
new file mode 100644
index 0000000..68e2a29
--- /dev/null
+++ b/usb/gadget/1.1/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.
+ */
+
+cc_binary {
+    name: "android.hardware.usb.gadget@1.1-service",
+    defaults: ["hidl_defaults"],
+    relative_install_path: "hw",
+    init_rc: ["android.hardware.usb.gadget@1.1-service.rc"],
+    vintf_fragments: ["android.hardware.usb.gadget@1.1-service.xml"],
+    vendor: true,
+    srcs: [
+        "service.cpp",
+        "UsbGadget.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.usb.gadget@1.0",
+        "android.hardware.usb.gadget@1.1",
+        "libbase",
+        "libcutils",
+        "libhardware",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: ["libusbconfigfs"],
+}
diff --git a/usb/gadget/1.1/default/UsbGadget.cpp b/usb/gadget/1.1/default/UsbGadget.cpp
new file mode 100644
index 0000000..36d865d
--- /dev/null
+++ b/usb/gadget/1.1/default/UsbGadget.cpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.usb.gadget@1.1-service"
+
+#include "UsbGadget.h"
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/inotify.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace android {
+namespace hardware {
+namespace usb {
+namespace gadget {
+namespace V1_1 {
+namespace implementation {
+
+UsbGadget::UsbGadget() {
+    if (access(OS_DESC_PATH, R_OK) != 0) {
+        ALOGE("configfs setup not done yet");
+        abort();
+    }
+}
+
+void currentFunctionsAppliedCallback(bool functionsApplied, void* payload) {
+    UsbGadget* gadget = (UsbGadget*)payload;
+    gadget->mCurrentUsbFunctionsApplied = functionsApplied;
+}
+
+Return<void> UsbGadget::getCurrentUsbFunctions(const sp<V1_0::IUsbGadgetCallback>& callback) {
+    Return<void> ret = callback->getCurrentUsbFunctionsCb(
+            mCurrentUsbFunctions, mCurrentUsbFunctionsApplied ? Status::FUNCTIONS_APPLIED
+                                                              : Status::FUNCTIONS_NOT_APPLIED);
+    if (!ret.isOk()) ALOGE("Call to getCurrentUsbFunctionsCb failed %s", ret.description().c_str());
+
+    return Void();
+}
+
+V1_0::Status UsbGadget::tearDownGadget() {
+    if (resetGadget() != V1_0::Status::SUCCESS) return V1_0::Status::ERROR;
+
+    if (monitorFfs.isMonitorRunning()) {
+        monitorFfs.reset();
+    } else {
+        ALOGI("mMonitor not running");
+    }
+    return V1_0::Status::SUCCESS;
+}
+
+Return<Status> UsbGadget::reset() {
+    if (!WriteStringToFile("none", PULLUP_PATH)) {
+        ALOGI("Gadget cannot be pulled down");
+        return Status::ERROR;
+    }
+
+    return Status::SUCCESS;
+}
+
+static V1_0::Status validateAndSetVidPid(uint64_t functions) {
+    V1_0::Status ret = V1_0::Status::SUCCESS;
+
+    switch (functions) {
+        case static_cast<uint64_t>(V1_0::GadgetFunction::MTP):
+            ret = setVidPid("0x18d1", "0x4ee1");
+            break;
+        case V1_0::GadgetFunction::ADB | V1_0::GadgetFunction::MTP:
+            ret = setVidPid("0x18d1", "0x4ee2");
+            break;
+        case static_cast<uint64_t>(V1_0::GadgetFunction::RNDIS):
+            ret = setVidPid("0x18d1", "0x4ee3");
+            break;
+        case V1_0::GadgetFunction::ADB | V1_0::GadgetFunction::RNDIS:
+            ret = setVidPid("0x18d1", "0x4ee4");
+            break;
+        case static_cast<uint64_t>(V1_0::GadgetFunction::PTP):
+            ret = setVidPid("0x18d1", "0x4ee5");
+            break;
+        case V1_0::GadgetFunction::ADB | V1_0::GadgetFunction::PTP:
+            ret = setVidPid("0x18d1", "0x4ee6");
+            break;
+        case static_cast<uint64_t>(V1_0::GadgetFunction::ADB):
+            ret = setVidPid("0x18d1", "0x4ee7");
+            break;
+        case static_cast<uint64_t>(V1_0::GadgetFunction::MIDI):
+            ret = setVidPid("0x18d1", "0x4ee8");
+            break;
+        case V1_0::GadgetFunction::ADB | V1_0::GadgetFunction::MIDI:
+            ret = setVidPid("0x18d1", "0x4ee9");
+            break;
+        case static_cast<uint64_t>(V1_0::GadgetFunction::ACCESSORY):
+            ret = setVidPid("0x18d1", "0x2d00");
+            break;
+        case V1_0::GadgetFunction::ADB | V1_0::GadgetFunction::ACCESSORY:
+            ret = setVidPid("0x18d1", "0x2d01");
+            break;
+        case static_cast<uint64_t>(V1_0::GadgetFunction::AUDIO_SOURCE):
+            ret = setVidPid("0x18d1", "0x2d02");
+            break;
+        case V1_0::GadgetFunction::ADB | V1_0::GadgetFunction::AUDIO_SOURCE:
+            ret = setVidPid("0x18d1", "0x2d03");
+            break;
+        case V1_0::GadgetFunction::ACCESSORY | V1_0::GadgetFunction::AUDIO_SOURCE:
+            ret = setVidPid("0x18d1", "0x2d04");
+            break;
+        case V1_0::GadgetFunction::ADB | V1_0::GadgetFunction::ACCESSORY |
+                V1_0::GadgetFunction::AUDIO_SOURCE:
+            ret = setVidPid("0x18d1", "0x2d05");
+            break;
+        default:
+            ALOGE("Combination not supported");
+            ret = V1_0::Status::CONFIGURATION_NOT_SUPPORTED;
+    }
+    return ret;
+}
+
+V1_0::Status UsbGadget::setupFunctions(uint64_t functions,
+                                       const sp<V1_0::IUsbGadgetCallback>& callback,
+                                       uint64_t timeout) {
+    bool ffsEnabled = false;
+    int i = 0;
+
+    if (addGenericAndroidFunctions(&monitorFfs, functions, &ffsEnabled, &i) !=
+        V1_0::Status::SUCCESS)
+        return V1_0::Status::ERROR;
+
+    if ((functions & V1_0::GadgetFunction::ADB) != 0) {
+        ffsEnabled = true;
+        if (addAdb(&monitorFfs, &i) != V1_0::Status::SUCCESS) return V1_0::Status::ERROR;
+    }
+
+    // Pull up the gadget right away when there are no ffs functions.
+    if (!ffsEnabled) {
+        if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) return V1_0::Status::ERROR;
+        mCurrentUsbFunctionsApplied = true;
+        if (callback) callback->setCurrentUsbFunctionsCb(functions, V1_0::Status::SUCCESS);
+        return V1_0::Status::SUCCESS;
+    }
+
+    monitorFfs.registerFunctionsAppliedCallback(&currentFunctionsAppliedCallback, this);
+    // Monitors the ffs paths to pull up the gadget when descriptors are written.
+    // Also takes of the pulling up the gadget again if the userspace process
+    // dies and restarts.
+    monitorFfs.startMonitor();
+
+    if (kDebug) ALOGI("Mainthread in Cv");
+
+    if (callback) {
+        bool pullup = monitorFfs.waitForPullUp(timeout);
+        Return<void> ret = callback->setCurrentUsbFunctionsCb(
+                functions, pullup ? V1_0::Status::SUCCESS : V1_0::Status::ERROR);
+        if (!ret.isOk()) ALOGE("setCurrentUsbFunctionsCb error %s", ret.description().c_str());
+    }
+
+    return V1_0::Status::SUCCESS;
+}
+
+Return<void> UsbGadget::setCurrentUsbFunctions(uint64_t functions,
+                                               const sp<V1_0::IUsbGadgetCallback>& callback,
+                                               uint64_t timeout) {
+    std::unique_lock<std::mutex> lk(mLockSetCurrentFunction);
+
+    mCurrentUsbFunctions = functions;
+    mCurrentUsbFunctionsApplied = false;
+
+    // Unlink the gadget and stop the monitor if running.
+    V1_0::Status status = tearDownGadget();
+    if (status != V1_0::Status::SUCCESS) {
+        goto error;
+    }
+
+    ALOGI("Returned from tearDown gadget");
+
+    // Leave the gadget pulled down to give time for the host to sense disconnect.
+    usleep(kDisconnectWaitUs);
+
+    if (functions == static_cast<uint64_t>(V1_0::GadgetFunction::NONE)) {
+        if (callback == NULL) return Void();
+        Return<void> ret = callback->setCurrentUsbFunctionsCb(functions, V1_0::Status::SUCCESS);
+        if (!ret.isOk())
+            ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.description().c_str());
+        return Void();
+    }
+
+    status = validateAndSetVidPid(functions);
+
+    if (status != V1_0::Status::SUCCESS) {
+        goto error;
+    }
+
+    status = setupFunctions(functions, callback, timeout);
+    if (status != V1_0::Status::SUCCESS) {
+        goto error;
+    }
+
+    ALOGI("Usb Gadget setcurrent functions called successfully");
+    return Void();
+
+error:
+    ALOGI("Usb Gadget setcurrent functions failed");
+    if (callback == NULL) return Void();
+    Return<void> ret = callback->setCurrentUsbFunctionsCb(functions, status);
+    if (!ret.isOk())
+        ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.description().c_str());
+    return Void();
+}
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace gadget
+}  // namespace usb
+}  // namespace hardware
+}  // namespace android
diff --git a/usb/gadget/1.1/default/UsbGadget.h b/usb/gadget/1.1/default/UsbGadget.h
new file mode 100644
index 0000000..b278071
--- /dev/null
+++ b/usb/gadget/1.1/default/UsbGadget.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_USB_GADGET_V1_1_USBGADGET_H
+#define ANDROID_HARDWARE_USB_GADGET_V1_1_USBGADGET_H
+
+#include <UsbGadgetCommon.h>
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+#include <android/hardware/usb/gadget/1.1/IUsbGadget.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <utils/Log.h>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace usb {
+namespace gadget {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::base::GetProperty;
+using ::android::base::SetProperty;
+using ::android::base::unique_fd;
+using ::android::base::WriteStringToFile;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::usb::gadget::addAdb;
+using ::android::hardware::usb::gadget::addEpollFd;
+using ::android::hardware::usb::gadget::getVendorFunctions;
+using ::android::hardware::usb::gadget::kDebug;
+using ::android::hardware::usb::gadget::kDisconnectWaitUs;
+using ::android::hardware::usb::gadget::linkFunction;
+using ::android::hardware::usb::gadget::MonitorFfs;
+using ::android::hardware::usb::gadget::resetGadget;
+using ::android::hardware::usb::gadget::setVidPid;
+using ::android::hardware::usb::gadget::unlinkFunctions;
+using ::std::string;
+
+constexpr char kGadgetName[] = "a600000.dwc3";
+static MonitorFfs monitorFfs(kGadgetName);
+
+struct UsbGadget : public IUsbGadget {
+    UsbGadget();
+
+    // Makes sure that only one request is processed at a time.
+    std::mutex mLockSetCurrentFunction;
+    uint64_t mCurrentUsbFunctions;
+    bool mCurrentUsbFunctionsApplied;
+
+    Return<void> setCurrentUsbFunctions(uint64_t functions,
+                                        const sp<V1_0::IUsbGadgetCallback>& callback,
+                                        uint64_t timeout) override;
+
+    Return<void> getCurrentUsbFunctions(const sp<V1_0::IUsbGadgetCallback>& callback) override;
+
+    Return<Status> reset() override;
+
+  private:
+    V1_0::Status tearDownGadget();
+    V1_0::Status setupFunctions(uint64_t functions, const sp<V1_0::IUsbGadgetCallback>& callback,
+                                uint64_t timeout);
+};
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace gadget
+}  // namespace usb
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_USB_V1_1_USBGADGET_H
diff --git a/usb/gadget/1.1/default/android.hardware.usb.gadget@1.1-service.rc b/usb/gadget/1.1/default/android.hardware.usb.gadget@1.1-service.rc
new file mode 100644
index 0000000..34ea7da
--- /dev/null
+++ b/usb/gadget/1.1/default/android.hardware.usb.gadget@1.1-service.rc
@@ -0,0 +1,6 @@
+service vendor.usb-gadget-hal-1-1 /vendor/bin/hw/android.hardware.usb.gadget@1.1-service
+    interface android.hardware.usb.gadget@1.0::IUsbGadget default
+    interface android.hardware.usb.gadget@1.1::IUsbGadget default
+    class hal
+    user root
+    group root shell mtp
diff --git a/usb/gadget/1.1/default/android.hardware.usb.gadget@1.1-service.xml b/usb/gadget/1.1/default/android.hardware.usb.gadget@1.1-service.xml
new file mode 100644
index 0000000..b40fa77
--- /dev/null
+++ b/usb/gadget/1.1/default/android.hardware.usb.gadget@1.1-service.xml
@@ -0,0 +1,12 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.usb.gadget</name>
+        <transport>hwbinder</transport>
+        <version>1.1</version>
+        <interface>
+            <name>IUsbGadget</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
+
diff --git a/usb/gadget/1.1/default/lib/Android.bp b/usb/gadget/1.1/default/lib/Android.bp
new file mode 100644
index 0000000..bba8340
--- /dev/null
+++ b/usb/gadget/1.1/default/lib/Android.bp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_library_static {
+    name: "libusbconfigfs",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+
+    srcs: [
+        "UsbGadgetUtils.cpp",
+        "MonitorFfs.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "android.hardware.usb.gadget@1.0",
+        "android.hardware.usb.gadget@1.1",
+        "libbase",
+        "libcutils",
+        "libhidlbase",
+        "libutils",
+    ],
+}
diff --git a/usb/gadget/1.1/default/lib/MonitorFfs.cpp b/usb/gadget/1.1/default/lib/MonitorFfs.cpp
new file mode 100644
index 0000000..0cdf038
--- /dev/null
+++ b/usb/gadget/1.1/default/lib/MonitorFfs.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "libusbconfigfs"
+
+#include "include/UsbGadgetCommon.h"
+
+namespace android {
+namespace hardware {
+namespace usb {
+namespace gadget {
+
+static volatile bool gadgetPullup;
+
+MonitorFfs::MonitorFfs(const char* const gadget)
+    : mWatchFd(),
+      mEndpointList(),
+      mLock(),
+      mCv(),
+      mLockFd(),
+      mCurrentUsbFunctionsApplied(false),
+      mMonitor(),
+      mCallback(NULL),
+      mPayload(NULL),
+      mGadgetName(gadget),
+      mMonitorRunning(false) {
+    unique_fd eventFd(eventfd(0, 0));
+    if (eventFd == -1) {
+        ALOGE("mEventFd failed to create %d", errno);
+        abort();
+    }
+
+    unique_fd epollFd(epoll_create(2));
+    if (epollFd == -1) {
+        ALOGE("mEpollFd failed to create %d", errno);
+        abort();
+    }
+
+    unique_fd inotifyFd(inotify_init());
+    if (inotifyFd < 0) {
+        ALOGE("inotify init failed");
+        abort();
+    }
+
+    if (addEpollFd(epollFd, inotifyFd) == -1) abort();
+
+    if (addEpollFd(epollFd, eventFd) == -1) abort();
+
+    mEpollFd = move(epollFd);
+    mInotifyFd = move(inotifyFd);
+    mEventFd = move(eventFd);
+    gadgetPullup = false;
+}
+
+static void displayInotifyEvent(struct inotify_event* i) {
+    ALOGE("    wd =%2d; ", i->wd);
+    if (i->cookie > 0) ALOGE("cookie =%4d; ", i->cookie);
+
+    ALOGE("mask = ");
+    if (i->mask & IN_ACCESS) ALOGE("IN_ACCESS ");
+    if (i->mask & IN_ATTRIB) ALOGE("IN_ATTRIB ");
+    if (i->mask & IN_CLOSE_NOWRITE) ALOGE("IN_CLOSE_NOWRITE ");
+    if (i->mask & IN_CLOSE_WRITE) ALOGE("IN_CLOSE_WRITE ");
+    if (i->mask & IN_CREATE) ALOGE("IN_CREATE ");
+    if (i->mask & IN_DELETE) ALOGE("IN_DELETE ");
+    if (i->mask & IN_DELETE_SELF) ALOGE("IN_DELETE_SELF ");
+    if (i->mask & IN_IGNORED) ALOGE("IN_IGNORED ");
+    if (i->mask & IN_ISDIR) ALOGE("IN_ISDIR ");
+    if (i->mask & IN_MODIFY) ALOGE("IN_MODIFY ");
+    if (i->mask & IN_MOVE_SELF) ALOGE("IN_MOVE_SELF ");
+    if (i->mask & IN_MOVED_FROM) ALOGE("IN_MOVED_FROM ");
+    if (i->mask & IN_MOVED_TO) ALOGE("IN_MOVED_TO ");
+    if (i->mask & IN_OPEN) ALOGE("IN_OPEN ");
+    if (i->mask & IN_Q_OVERFLOW) ALOGE("IN_Q_OVERFLOW ");
+    if (i->mask & IN_UNMOUNT) ALOGE("IN_UNMOUNT ");
+    ALOGE("\n");
+
+    if (i->len > 0) ALOGE("        name = %s\n", i->name);
+}
+
+void* MonitorFfs::startMonitorFd(void* param) {
+    MonitorFfs* monitorFfs = (MonitorFfs*)param;
+    char buf[kBufferSize];
+    bool writeUdc = true, stopMonitor = false;
+    struct epoll_event events[kEpollEvents];
+    steady_clock::time_point disconnect;
+
+    bool descriptorWritten = true;
+    for (int i = 0; i < static_cast<int>(monitorFfs->mEndpointList.size()); i++) {
+        if (access(monitorFfs->mEndpointList.at(i).c_str(), R_OK)) {
+            descriptorWritten = false;
+            break;
+        }
+    }
+
+    // notify here if the endpoints are already present.
+    if (descriptorWritten) {
+        usleep(kPullUpDelay);
+        if (!!WriteStringToFile(monitorFfs->mGadgetName, PULLUP_PATH)) {
+            lock_guard<mutex> lock(monitorFfs->mLock);
+            monitorFfs->mCurrentUsbFunctionsApplied = true;
+            monitorFfs->mCallback(monitorFfs->mCurrentUsbFunctionsApplied, monitorFfs->mPayload);
+            gadgetPullup = true;
+            writeUdc = false;
+            ALOGI("GADGET pulled up");
+            monitorFfs->mCv.notify_all();
+        }
+    }
+
+    while (!stopMonitor) {
+        int nrEvents = epoll_wait(monitorFfs->mEpollFd, events, kEpollEvents, -1);
+
+        if (nrEvents <= 0) {
+            ALOGE("epoll wait did not return descriptor number");
+            continue;
+        }
+
+        for (int i = 0; i < nrEvents; i++) {
+            ALOGI("event=%u on fd=%d\n", events[i].events, events[i].data.fd);
+
+            if (events[i].data.fd == monitorFfs->mInotifyFd) {
+                // Process all of the events in buffer returned by read().
+                int numRead = read(monitorFfs->mInotifyFd, buf, kBufferSize);
+                for (char* p = buf; p < buf + numRead;) {
+                    struct inotify_event* event = (struct inotify_event*)p;
+                    if (kDebug) displayInotifyEvent(event);
+
+                    p += sizeof(struct inotify_event) + event->len;
+
+                    bool descriptorPresent = true;
+                    for (int j = 0; j < static_cast<int>(monitorFfs->mEndpointList.size()); j++) {
+                        if (access(monitorFfs->mEndpointList.at(j).c_str(), R_OK)) {
+                            if (kDebug) ALOGI("%s absent", monitorFfs->mEndpointList.at(j).c_str());
+                            descriptorPresent = false;
+                            break;
+                        }
+                    }
+
+                    if (!descriptorPresent && !writeUdc) {
+                        if (kDebug) ALOGI("endpoints not up");
+                        writeUdc = true;
+                        disconnect = std::chrono::steady_clock::now();
+                    } else if (descriptorPresent && writeUdc) {
+                        steady_clock::time_point temp = steady_clock::now();
+
+                        if (std::chrono::duration_cast<microseconds>(temp - disconnect).count() <
+                            kPullUpDelay)
+                            usleep(kPullUpDelay);
+
+                        if (!!WriteStringToFile(monitorFfs->mGadgetName, PULLUP_PATH)) {
+                            lock_guard<mutex> lock(monitorFfs->mLock);
+                            monitorFfs->mCurrentUsbFunctionsApplied = true;
+                            monitorFfs->mCallback(monitorFfs->mCurrentUsbFunctionsApplied,
+                                                  monitorFfs->mPayload);
+                            ALOGI("GADGET pulled up");
+                            writeUdc = false;
+                            gadgetPullup = true;
+                            // notify the main thread to signal userspace.
+                            monitorFfs->mCv.notify_all();
+                        }
+                    }
+                }
+            } else {
+                uint64_t flag;
+                read(monitorFfs->mEventFd, &flag, sizeof(flag));
+                if (flag == 100) {
+                    stopMonitor = true;
+                    break;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+void MonitorFfs::reset() {
+    lock_guard<mutex> lock(mLockFd);
+    uint64_t flag = 100;
+    unsigned long ret;
+
+    if (mMonitorRunning) {
+        // Stop the monitor thread by writing into signal fd.
+        ret = TEMP_FAILURE_RETRY(write(mEventFd, &flag, sizeof(flag)));
+        if (ret < 0) ALOGE("Error writing eventfd errno=%d", errno);
+
+        ALOGI("mMonitor signalled to exit");
+        mMonitor->join();
+        ALOGI("mMonitor destroyed");
+        mMonitorRunning = false;
+    }
+
+    for (std::vector<int>::size_type i = 0; i != mWatchFd.size(); i++)
+        inotify_rm_watch(mInotifyFd, mWatchFd[i]);
+
+    mEndpointList.clear();
+    gadgetPullup = false;
+    mCallback = NULL;
+    mPayload = NULL;
+}
+
+bool MonitorFfs::startMonitor() {
+    mMonitor = unique_ptr<thread>(new thread(this->startMonitorFd, this));
+    mMonitorRunning = true;
+    return true;
+}
+
+bool MonitorFfs::isMonitorRunning() {
+    return mMonitorRunning;
+}
+
+bool MonitorFfs::waitForPullUp(int timeout_ms) {
+    std::unique_lock<std::mutex> lk(mLock);
+
+    if (gadgetPullup) return true;
+
+    if (mCv.wait_for(lk, timeout_ms * 1ms, [] { return gadgetPullup; })) {
+        ALOGI("monitorFfs signalled true");
+        return true;
+    } else {
+        ALOGI("monitorFfs signalled error");
+        // continue monitoring as the descriptors might be written at a later
+        // point.
+        return false;
+    }
+}
+
+bool MonitorFfs::addInotifyFd(string fd) {
+    lock_guard<mutex> lock(mLockFd);
+    int wfd;
+
+    wfd = inotify_add_watch(mInotifyFd, fd.c_str(), IN_ALL_EVENTS);
+    if (wfd == -1)
+        return false;
+    else
+        mWatchFd.push_back(wfd);
+
+    return true;
+}
+
+void MonitorFfs::addEndPoint(string ep) {
+    lock_guard<mutex> lock(mLockFd);
+
+    mEndpointList.push_back(ep);
+}
+
+void MonitorFfs::registerFunctionsAppliedCallback(void (*callback)(bool functionsApplied,
+                                                                   void* payload),
+                                                  void* payload) {
+    mCallback = callback;
+    mPayload = payload;
+}
+
+}  // namespace gadget
+}  // namespace usb
+}  // namespace hardware
+}  // namespace android
diff --git a/usb/gadget/1.1/default/lib/UsbGadgetUtils.cpp b/usb/gadget/1.1/default/lib/UsbGadgetUtils.cpp
new file mode 100644
index 0000000..8402853
--- /dev/null
+++ b/usb/gadget/1.1/default/lib/UsbGadgetUtils.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "libusbconfigfs"
+
+#include "include/UsbGadgetCommon.h"
+
+namespace android {
+namespace hardware {
+namespace usb {
+namespace gadget {
+
+int unlinkFunctions(const char* path) {
+    DIR* config = opendir(path);
+    struct dirent* function;
+    char filepath[kMaxFilePathLength];
+    int ret = 0;
+
+    if (config == NULL) return -1;
+
+    // d_type does not seems to be supported in /config
+    // so filtering by name.
+    while (((function = readdir(config)) != NULL)) {
+        if ((strstr(function->d_name, FUNCTION_NAME) == NULL)) continue;
+        // build the path for each file in the folder.
+        sprintf(filepath, "%s/%s", path, function->d_name);
+        ret = remove(filepath);
+        if (ret) {
+            ALOGE("Unable  remove file %s errno:%d", filepath, errno);
+            break;
+        }
+    }
+
+    closedir(config);
+    return ret;
+}
+
+int addEpollFd(const unique_fd& epfd, const unique_fd& fd) {
+    struct epoll_event event;
+    int ret;
+
+    event.data.fd = fd;
+    event.events = EPOLLIN;
+
+    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
+    if (ret) ALOGE("epoll_ctl error %d", errno);
+
+    return ret;
+}
+
+int linkFunction(const char* function, int index) {
+    char functionPath[kMaxFilePathLength];
+    char link[kMaxFilePathLength];
+
+    sprintf(functionPath, "%s%s", FUNCTIONS_PATH, function);
+    sprintf(link, "%s%d", FUNCTION_PATH, index);
+    if (symlink(functionPath, link)) {
+        ALOGE("Cannot create symlink %s -> %s errno:%d", link, functionPath, errno);
+        return -1;
+    }
+    return 0;
+}
+
+Status setVidPid(const char* vid, const char* pid) {
+    if (!WriteStringToFile(vid, VENDOR_ID_PATH)) return Status::ERROR;
+
+    if (!WriteStringToFile(pid, PRODUCT_ID_PATH)) return Status::ERROR;
+
+    return Status::SUCCESS;
+}
+
+std::string getVendorFunctions() {
+    if (GetProperty(kBuildType, "") == "user") return "user";
+
+    std::string bootMode = GetProperty(PERSISTENT_BOOT_MODE, "");
+    std::string persistVendorFunctions = GetProperty(kPersistentVendorConfig, "");
+    std::string vendorFunctions = GetProperty(kVendorConfig, "");
+    std::string ret = "";
+
+    if (vendorFunctions != "") {
+        ret = vendorFunctions;
+    } else if (bootMode == "usbradio" || bootMode == "factory" || bootMode == "ffbm-00" ||
+               bootMode == "ffbm-01") {
+        if (persistVendorFunctions != "")
+            ret = persistVendorFunctions;
+        else
+            ret = "diag";
+        // vendor.usb.config will reflect the current configured functions
+        SetProperty(kVendorConfig, ret);
+    }
+
+    return ret;
+}
+
+Status resetGadget() {
+    ALOGI("setCurrentUsbFunctions None");
+
+    if (!WriteStringToFile("none", PULLUP_PATH)) ALOGI("Gadget cannot be pulled down");
+
+    if (!WriteStringToFile("0", DEVICE_CLASS_PATH)) return Status::ERROR;
+
+    if (!WriteStringToFile("0", DEVICE_SUB_CLASS_PATH)) return Status::ERROR;
+
+    if (!WriteStringToFile("0", DEVICE_PROTOCOL_PATH)) return Status::ERROR;
+
+    if (!WriteStringToFile("0", DESC_USE_PATH)) return Status::ERROR;
+
+    if (unlinkFunctions(CONFIG_PATH)) return Status::ERROR;
+
+    return Status::SUCCESS;
+}
+
+Status addGenericAndroidFunctions(MonitorFfs* monitorFfs, uint64_t functions, bool* ffsEnabled,
+                                  int* functionCount) {
+    if (((functions & GadgetFunction::MTP) != 0)) {
+        *ffsEnabled = true;
+        ALOGI("setCurrentUsbFunctions mtp");
+        if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR;
+
+        if (!monitorFfs->addInotifyFd("/dev/usb-ffs/mtp/")) return Status::ERROR;
+
+        if (linkFunction("ffs.mtp", (*functionCount)++)) return Status::ERROR;
+
+        // Add endpoints to be monitored.
+        monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep1");
+        monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep2");
+        monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep3");
+    } else if (((functions & GadgetFunction::PTP) != 0)) {
+        *ffsEnabled = true;
+        ALOGI("setCurrentUsbFunctions ptp");
+        if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR;
+
+        if (!monitorFfs->addInotifyFd("/dev/usb-ffs/ptp/")) return Status::ERROR;
+
+        if (linkFunction("ffs.ptp", (*functionCount)++)) return Status::ERROR;
+
+        // Add endpoints to be monitored.
+        monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep1");
+        monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep2");
+        monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep3");
+    }
+
+    if ((functions & GadgetFunction::MIDI) != 0) {
+        ALOGI("setCurrentUsbFunctions MIDI");
+        if (linkFunction("midi.gs5", (*functionCount)++)) return Status::ERROR;
+    }
+
+    if ((functions & GadgetFunction::ACCESSORY) != 0) {
+        ALOGI("setCurrentUsbFunctions Accessory");
+        if (linkFunction("accessory.gs2", (*functionCount)++)) return Status::ERROR;
+    }
+
+    if ((functions & GadgetFunction::AUDIO_SOURCE) != 0) {
+        ALOGI("setCurrentUsbFunctions Audio Source");
+        if (linkFunction("audio_source.gs3", (*functionCount)++)) return Status::ERROR;
+    }
+
+    if ((functions & GadgetFunction::RNDIS) != 0) {
+        ALOGI("setCurrentUsbFunctions rndis");
+        if (linkFunction("gsi.rndis", (*functionCount)++)) return Status::ERROR;
+    }
+
+    return Status::SUCCESS;
+}
+
+Status addAdb(MonitorFfs* monitorFfs, int* functionCount) {
+    ALOGI("setCurrentUsbFunctions Adb");
+    if (!monitorFfs->addInotifyFd("/dev/usb-ffs/adb/")) return Status::ERROR;
+
+    if (linkFunction("ffs.adb", (*functionCount)++)) return Status::ERROR;
+    monitorFfs->addEndPoint("/dev/usb-ffs/adb/ep1");
+    monitorFfs->addEndPoint("/dev/usb-ffs/adb/ep2");
+    ALOGI("Service started");
+    return Status::SUCCESS;
+}
+
+}  // namespace gadget
+}  // namespace usb
+}  // namespace hardware
+}  // namespace android
diff --git a/usb/gadget/1.1/default/lib/include/UsbGadgetCommon.h b/usb/gadget/1.1/default/lib/include/UsbGadgetCommon.h
new file mode 100644
index 0000000..b30f18e
--- /dev/null
+++ b/usb/gadget/1.1/default/lib/include/UsbGadgetCommon.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 HARDWARE_USB_USBGADGETCOMMON_H
+#define HARDWARE_USB_USBGADGETCOMMON_H
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+
+#include <android/hardware/usb/gadget/1.1/IUsbGadget.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/inotify.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <utils/Log.h>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace usb {
+namespace gadget {
+
+constexpr int kBufferSize = 512;
+constexpr int kMaxFilePathLength = 256;
+constexpr int kEpollEvents = 10;
+constexpr bool kDebug = false;
+constexpr int kDisconnectWaitUs = 100000;
+constexpr int kPullUpDelay = 500000;
+constexpr int kShutdownMonitor = 100;
+
+constexpr char kBuildType[] = "ro.build.type";
+constexpr char kPersistentVendorConfig[] = "persist.vendor.usb.usbradio.config";
+constexpr char kVendorConfig[] = "vendor.usb.config";
+
+#define GADGET_PATH "/config/usb_gadget/g1/"
+#define PULLUP_PATH GADGET_PATH "UDC"
+#define PERSISTENT_BOOT_MODE "ro.bootmode"
+#define VENDOR_ID_PATH GADGET_PATH "idVendor"
+#define PRODUCT_ID_PATH GADGET_PATH "idProduct"
+#define DEVICE_CLASS_PATH GADGET_PATH "bDeviceClass"
+#define DEVICE_SUB_CLASS_PATH GADGET_PATH "bDeviceSubClass"
+#define DEVICE_PROTOCOL_PATH GADGET_PATH "bDeviceProtocol"
+#define DESC_USE_PATH GADGET_PATH "os_desc/use"
+#define OS_DESC_PATH GADGET_PATH "os_desc/b.1"
+#define CONFIG_PATH GADGET_PATH "configs/b.1/"
+#define FUNCTIONS_PATH GADGET_PATH "functions/"
+#define FUNCTION_NAME "function"
+#define FUNCTION_PATH CONFIG_PATH FUNCTION_NAME
+#define RNDIS_PATH FUNCTIONS_PATH "gsi.rndis"
+
+using ::android::base::GetProperty;
+using ::android::base::SetProperty;
+using ::android::base::unique_fd;
+using ::android::base::WriteStringToFile;
+using ::android::hardware::usb::gadget::V1_0::GadgetFunction;
+using ::android::hardware::usb::gadget::V1_0::Status;
+
+using ::std::lock_guard;
+using ::std::move;
+using ::std::mutex;
+using ::std::string;
+using ::std::thread;
+using ::std::unique_ptr;
+using ::std::vector;
+using ::std::chrono::microseconds;
+using ::std::chrono::steady_clock;
+using ::std::literals::chrono_literals::operator""ms;
+
+// MonitorFfs automously manages gadget pullup by monitoring
+// the ep file status. Restarts the usb gadget when the ep
+// owner restarts.
+class MonitorFfs {
+  private:
+    // Monitors the endpoints Inotify events.
+    unique_fd mInotifyFd;
+    // Control pipe for shutting down the mMonitor thread.
+    // mMonitor exits when SHUTDOWN_MONITOR is written into
+    // mEventFd/
+    unique_fd mEventFd;
+    // Pools on mInotifyFd and mEventFd.
+    unique_fd mEpollFd;
+    vector<int> mWatchFd;
+
+    // Maintains the list of Endpoints.
+    vector<string> mEndpointList;
+    // protects the CV.
+    std::mutex mLock;
+    std::condition_variable mCv;
+    // protects mInotifyFd, mEpollFd.
+    std::mutex mLockFd;
+
+    // Flag to maintain the current status of gadget pullup.
+    bool mCurrentUsbFunctionsApplied;
+
+    // Thread object that executes the ep monitoring logic.
+    unique_ptr<thread> mMonitor;
+    // Callback to be invoked when gadget is pulled up.
+    void (*mCallback)(bool functionsApplied, void* payload);
+    void* mPayload;
+    // Name of the USB gadget. Used for pullup.
+    const char* const mGadgetName;
+    // Monitor State
+    bool mMonitorRunning;
+
+  public:
+    MonitorFfs(const char* const gadget);
+    // Inits all the UniqueFds.
+    void reset();
+    // Starts monitoring endpoints and pullup the gadget when
+    // the descriptors are written.
+    bool startMonitor();
+    // Waits for timeout_ms for gadget pull up to happen.
+    // Returns immediately if the gadget is already pulled up.
+    bool waitForPullUp(int timeout_ms);
+    // Adds the given fd to the watch list.
+    bool addInotifyFd(string fd);
+    // Adds the given endpoint to the watch list.
+    void addEndPoint(string ep);
+    // Registers the async callback from the caller to notify the caller
+    // when the gadget pull up happens.
+    void registerFunctionsAppliedCallback(void (*callback)(bool functionsApplied, void*(payload)),
+                                          void* payload);
+    bool isMonitorRunning();
+    // Ep monitoring and the gadget pull up logic.
+    static void* startMonitorFd(void* param);
+};
+
+//**************** Helper functions ************************//
+
+// Adds the given fd to the epollfd(epfd).
+int addEpollFd(const unique_fd& epfd, const unique_fd& fd);
+// Removes all the usb functions link in the specified path.
+int unlinkFunctions(const char* path);
+// Craetes a configfs link for the function.
+int linkFunction(const char* function, int index);
+// Sets the USB VID and PID.
+Status setVidPid(const char* vid, const char* pid);
+// Extracts vendor functions from the vendor init properties.
+std::string getVendorFunctions();
+// Adds Adb to the usb configuration.
+Status addAdb(MonitorFfs* monitorFfs, int* functionCount);
+// Adds all applicable generic android usb functions other than ADB.
+Status addGenericAndroidFunctions(MonitorFfs* monitorFfs, uint64_t functions, bool* ffsEnabled,
+                                  int* functionCount);
+// Pulls down USB gadget.
+Status resetGadget();
+
+}  // namespace gadget
+}  // namespace usb
+}  // namespace hardware
+}  // namespace android
+#endif
diff --git a/usb/gadget/1.1/default/service.cpp b/usb/gadget/1.1/default/service.cpp
new file mode 100644
index 0000000..7414e89
--- /dev/null
+++ b/usb/gadget/1.1/default/service.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.usb.gadget@1.1-service"
+
+#include <hidl/HidlTransportSupport.h>
+#include "UsbGadget.h"
+
+using android::sp;
+
+// libhwbinder:
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+// Generated HIDL files
+using android::hardware::usb::gadget::V1_1::IUsbGadget;
+using android::hardware::usb::gadget::V1_1::implementation::UsbGadget;
+
+using android::OK;
+using android::status_t;
+
+int main() {
+    configureRpcThreadpool(1, true /*callerWillJoin*/);
+
+    android::sp<IUsbGadget> service2 = new UsbGadget();
+
+    status_t status = service2->registerAsService();
+
+    if (status != OK) {
+        ALOGE("Cannot register USB Gadget HAL service");
+        return 1;
+    }
+
+    ALOGI("USB Gadget HAL Ready.");
+    joinRpcThreadpool();
+    // Under noraml cases, execution will not reach this line.
+    ALOGI("USB Gadget HAL failed to join thread pool.");
+    return 1;
+}
diff --git a/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl
index b9a80ec..0fdfa5d 100644
--- a/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl
@@ -49,4 +49,9 @@
      * A haptic effect that simulates quick downwards movement with gravity.
      */
     QUICK_FALL,
+    /**
+     * This very short effect should produce a light crisp sensation intended
+     * to be used repetitively for dynamic feedback.
+     */
+    LIGHT_TICK,
 }
diff --git a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
index f553664..06a8bf5 100644
--- a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
@@ -158,12 +158,31 @@
     int getCompositionSizeMax();
 
     /**
+     * List of supported effect primitive.
+     *
+     * Return the effect primitives which are supported by the compose API.
+     * Implementations are expected to support all primitives of the interface
+     * version that they implement.
+     */
+    CompositePrimitive[] getSupportedPrimitives();
+
+    /**
+     * Retrieve effect primitive's duration in milliseconds.
+     *
+     * Support is reflected in getCapabilities (CAP_COMPOSE_EFFECTS).
+     *
+     * @return Best effort estimation of effect primitive's duration.
+     * @param primitive Effect primitive being queried.
+     */
+    int getPrimitiveDuration(CompositePrimitive primitive);
+
+    /**
      * Fire off a string of effect primitives, combined to perform richer effects.
      *
      * Support is reflected in getCapabilities (CAP_COMPOSE_EFFECTS).
      *
      * Doing this operation while the vibrator is already on is undefined behavior. Clients should
-     * explicitly call off.
+     * explicitly call off. IVibratorCallback.onComplete() support is required for this API.
      *
      * @param composite Array of composition parameters.
      */
diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp
index cedd9cb..0d7131a 100644
--- a/vibrator/aidl/default/Vibrator.cpp
+++ b/vibrator/aidl/default/Vibrator.cpp
@@ -113,6 +113,26 @@
     return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported) {
+    *supported = {
+            CompositePrimitive::NOOP,       CompositePrimitive::CLICK,
+            CompositePrimitive::THUD,       CompositePrimitive::SPIN,
+            CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE,
+            CompositePrimitive::QUICK_FALL, CompositePrimitive::LIGHT_TICK,
+    };
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
+                                                  int32_t* durationMs) {
+    if (primitive != CompositePrimitive::NOOP) {
+        *durationMs = 100;
+    } else {
+        *durationMs = 0;
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
 ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite,
                                      const std::shared_ptr<IVibratorCallback>& callback) {
     if (composite.size() > kComposeSizeMax) {
@@ -127,7 +147,7 @@
             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
         }
         if (e.primitive < CompositePrimitive::NOOP ||
-            e.primitive > CompositePrimitive::QUICK_FALL) {
+            e.primitive > CompositePrimitive::LIGHT_TICK) {
             return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
         }
     }
diff --git a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
index 0eb957d..c3f3616 100644
--- a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
+++ b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
@@ -36,6 +36,9 @@
     ndk::ScopedAStatus setExternalControl(bool enabled) override;
     ndk::ScopedAStatus getCompositionDelayMax(int32_t* maxDelayMs);
     ndk::ScopedAStatus getCompositionSizeMax(int32_t* maxSize);
+    ndk::ScopedAStatus getSupportedPrimitives(std::vector<CompositePrimitive>* supported) override;
+    ndk::ScopedAStatus getPrimitiveDuration(CompositePrimitive primitive,
+                                            int32_t* durationMs) override;
     ndk::ScopedAStatus compose(const std::vector<CompositeEffect>& composite,
                                const std::shared_ptr<IVibratorCallback>& callback) override;
     ndk::ScopedAStatus getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return) override;
diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
index f197763..411fe7a 100644
--- a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
+++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
@@ -261,6 +261,29 @@
     }
 }
 
+TEST_P(VibratorAidl, GetSupportedPrimitives) {
+    if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
+        std::vector<CompositePrimitive> supported;
+
+        EXPECT_EQ(Status::EX_NONE, vibrator->getSupportedPrimitives(&supported).exceptionCode());
+
+        std::sort(supported.begin(), supported.end());
+
+        EXPECT_EQ(kCompositePrimitives, supported);
+    }
+}
+
+TEST_P(VibratorAidl, GetPrimitiveDuration) {
+    if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
+        int32_t duration;
+
+        for (auto primitive : kCompositePrimitives) {
+            EXPECT_EQ(Status::EX_NONE,
+                      vibrator->getPrimitiveDuration(primitive, &duration).exceptionCode());
+        }
+    }
+}
+
 TEST_P(VibratorAidl, ComposeValidPrimitives) {
     if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
         int32_t maxDelay, maxSize;
@@ -357,6 +380,30 @@
     }
 }
 
+TEST_P(VibratorAidl, ComposeCallback) {
+    if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
+        std::promise<void> completionPromise;
+        std::future<void> completionFuture{completionPromise.get_future()};
+        sp<CompletionCallback> callback =
+                new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+        CompositePrimitive primitive = CompositePrimitive::CLICK;
+        CompositeEffect effect;
+        std::vector<CompositeEffect> composite;
+        int32_t duration;
+
+        effect.delayMs = 0;
+        effect.primitive = primitive;
+        effect.scale = 1.0f;
+        composite.emplace_back(effect);
+
+        EXPECT_EQ(Status::EX_NONE,
+                  vibrator->getPrimitiveDuration(primitive, &duration).exceptionCode());
+        EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, callback).exceptionCode());
+        EXPECT_EQ(completionFuture.wait_for(std::chrono::milliseconds(duration * 2)),
+                  std::future_status::ready);
+    }
+}
+
 TEST_P(VibratorAidl, AlwaysOn) {
     if (capabilities & IVibrator::CAP_ALWAYS_ON_CONTROL) {
         std::vector<Effect> supported;
diff --git a/wifi/1.4/default/wifi_legacy_hal.cpp b/wifi/1.4/default/wifi_legacy_hal.cpp
index 6f088d7..3ca3226 100644
--- a/wifi/1.4/default/wifi_legacy_hal.cpp
+++ b/wifi/1.4/default/wifi_legacy_hal.cpp
@@ -824,11 +824,10 @@
                                                     mode);
 }
 
-wifi_error WifiLegacyHal::setThermalMitigationMode(
-    const std::string& iface_name, wifi_thermal_mode mode,
-    uint32_t completion_window) {
+wifi_error WifiLegacyHal::setThermalMitigationMode(wifi_thermal_mode mode,
+                                                   uint32_t completion_window) {
     return global_func_table_.wifi_set_thermal_mitigation_mode(
-        getIfaceHandle(iface_name), mode, completion_window);
+        global_handle_, mode, completion_window);
 }
 
 std::pair<wifi_error, uint32_t> WifiLegacyHal::getLoggerSupportedFeatureSet(
diff --git a/wifi/1.4/default/wifi_legacy_hal.h b/wifi/1.4/default/wifi_legacy_hal.h
index 74cc84b..a7b40a0 100644
--- a/wifi/1.4/default/wifi_legacy_hal.h
+++ b/wifi/1.4/default/wifi_legacy_hal.h
@@ -259,8 +259,7 @@
     virtual wifi_error resetTxPowerScenario(const std::string& iface_name);
     wifi_error setLatencyMode(const std::string& iface_name,
                               wifi_latency_mode mode);
-    wifi_error setThermalMitigationMode(const std::string& iface_name,
-                                        wifi_thermal_mode mode,
+    wifi_error setThermalMitigationMode(wifi_thermal_mode mode,
                                         uint32_t completion_window);
     // Logger/debug functions.
     std::pair<wifi_error, uint32_t> getLoggerSupportedFeatureSet(
diff --git a/wifi/hostapd/1.2/IHostapd.hal b/wifi/hostapd/1.2/IHostapd.hal
index 5126d12..0869da0 100644
--- a/wifi/hostapd/1.2/IHostapd.hal
+++ b/wifi/hostapd/1.2/IHostapd.hal
@@ -91,12 +91,6 @@
         bool enableHeMultiUserBeamformer;
 
         /**
-         * Used BSS Color for softAp running in 802.11ax mode
-         * Note: this is only applicable if 802.11ax is supported for softAp
-         */
-        uint32_t heBssColor;
-
-        /**
          * Whether HE Target Wait Time (TWT) is enabled or not on softAp.
          * Note: this is only applicable if 802.11ax is supported for softAp
          */
diff --git a/wifi/supplicant/1.3/Android.bp b/wifi/supplicant/1.3/Android.bp
index 3f20531..15c72fe 100644
--- a/wifi/supplicant/1.3/Android.bp
+++ b/wifi/supplicant/1.3/Android.bp
@@ -17,6 +17,7 @@
         "android.hardware.wifi.supplicant@1.0",
         "android.hardware.wifi.supplicant@1.1",
         "android.hardware.wifi.supplicant@1.2",
+        "android.hardware.wifi@1.0",
         "android.hidl.base@1.0",
     ],
     gen_java: true,
diff --git a/wifi/supplicant/1.3/ISupplicantStaIface.hal b/wifi/supplicant/1.3/ISupplicantStaIface.hal
index 58ef165..b501a95 100644
--- a/wifi/supplicant/1.3/ISupplicantStaIface.hal
+++ b/wifi/supplicant/1.3/ISupplicantStaIface.hal
@@ -20,6 +20,7 @@
 import @1.2::ISupplicantStaIface;
 import ISupplicantStaNetwork;
 import ISupplicantStaIfaceCallback;
+import @1.0::MacAddress;
 
 /**
  * Interface exposed by the supplicant for each station mode network
@@ -90,4 +91,32 @@
      */
     getKeyMgmtCapabilities_1_3()
         generates (SupplicantStatus status, bitfield<KeyMgmtMask> keyMgmtMask);
+
+    /**
+     * Flush FILS HLP IEs
+     * Use this to flush all the HLP IEs in wpa_supplicant
+     *
+     * @return status Status of the operation.
+     *         Possible status codes:
+     *         |SupplicantStatusCode.SUCCESS|,
+     *         |SupplicantStatusCode.FAILURE_UNKNOWN|,
+     *         |SupplicantStatusCode.FAILURE_IFACE_INVALID|,
+     *         |SupplicantStatusCode.FAILURE_IFACE_DISABLED|
+     */
+    filsHlpFlushRequest() generates (SupplicantStatus status);
+
+    /**
+     * Add FILS HLP IEs
+     * Use this to add a HLP IE to wpa_supplicant
+     *
+     * @param dst_mac MAC address of the destination
+     * @param pkt The contents of the HLP IE starting from ethertype
+     * @return status Status of the operation.
+     *         Possible status codes:
+     *         |SupplicantStatusCode.SUCCESS|,
+     *         |SupplicantStatusCode.FAILURE_UNKNOWN|,
+     *         |SupplicantStatusCode.FAILURE_IFACE_INVALID|,
+     *         |SupplicantStatusCode.FAILURE_IFACE_DISABLED|
+     */
+    filsHlpAddRequest(MacAddress dst_mac, vec<uint8_t> pkt) generates (SupplicantStatus status);
 };
diff --git a/wifi/supplicant/1.3/ISupplicantStaIfaceCallback.hal b/wifi/supplicant/1.3/ISupplicantStaIfaceCallback.hal
index 72ba160..6828dcd 100644
--- a/wifi/supplicant/1.3/ISupplicantStaIfaceCallback.hal
+++ b/wifi/supplicant/1.3/ISupplicantStaIfaceCallback.hal
@@ -17,6 +17,10 @@
 package android.hardware.wifi.supplicant@1.3;
 
 import @1.2::ISupplicantStaIfaceCallback;
+import @1.0::ISupplicantStaIfaceCallback.State;
+import @1.0::Bssid;
+import @1.0::SupplicantNetworkId;
+import @1.0::Ssid;
 
 /**
  * Callback Interface exposed by the supplicant service
@@ -185,4 +189,31 @@
      * request frame.
      */
     oneway onBssTmHandlingDone(BssTmData tmData);
+
+    /**
+     * Indicates an EAP authentication failure.
+     */
+    oneway onEapFailure_1_3(uint32_t errorCode);
+
+    /**
+     * Used to indicate a state change event on this particular iface. If this
+     * event is triggered by a particular network, the |SupplicantNetworkId|,
+     * |ssid|, |bssid| parameters must indicate the parameters of the network/AP
+     * which caused this state transition.
+     *
+     * @param newState New State of the interface. This must be one of the |State|
+     *        values above.
+     * @param bssid BSSID of the corresponding AP which caused this state
+     *        change event. This must be zero'ed if this event is not
+     *        specific to a particular network.
+     * @param id ID of the corresponding network which caused this
+     *        state change event. This must be invalid (UINT32_MAX) if this
+     *        event is not specific to a particular network.
+     * @param ssid SSID of the corresponding network which caused this state
+     *        change event. This must be empty if this event is not specific
+     *        to a particular network.
+     * @param filsHlpSent If FILS HLP IEs were included in this association.
+     */
+    oneway onStateChanged_1_3(State newState, Bssid bssid, SupplicantNetworkId id, Ssid ssid,
+        bool filsHlpSent);
 };
diff --git a/wifi/supplicant/1.3/ISupplicantStaNetwork.hal b/wifi/supplicant/1.3/ISupplicantStaNetwork.hal
index e579d24..0566a21 100644
--- a/wifi/supplicant/1.3/ISupplicantStaNetwork.hal
+++ b/wifi/supplicant/1.3/ISupplicantStaNetwork.hal
@@ -44,6 +44,14 @@
          * WAPI Cert
          */
         WAPI_CERT = 1 << 13,
+        /**
+         * FILS shared key authentication with sha-256
+         */
+        FILS_SHA256 = 1 << 18,
+        /**
+         * FILS shared key authentication with sha-384
+         */
+        FILS_SHA384 = 1 << 19,
     };
 
     /**
@@ -268,4 +276,16 @@
      * @return authAlgMask Combination of |AuthAlgMask| values.
      */
     getAuthAlg_1_3() generates (SupplicantStatus status, bitfield<AuthAlgMask> authAlgMask);
+
+    /**
+     * Enable EAP ERP for this network.
+     *
+     * @param enable true to set, false otherwise.
+     * @return status Status of the operation.
+     *         Possible status codes:
+     *         |SupplicantStatusCode.SUCCESS|,
+     *         |SupplicantStatusCode.FAILURE_UNKNOWN|,
+     *         |SupplicantStatusCode.FAILURE_NETWORK_INVALID|
+     */
+    setEapErp(bool enable) generates (SupplicantStatus status);
 };
diff --git a/wifi/supplicant/1.3/types.hal b/wifi/supplicant/1.3/types.hal
index 05f4760..a4b2ff7 100644
--- a/wifi/supplicant/1.3/types.hal
+++ b/wifi/supplicant/1.3/types.hal
@@ -19,6 +19,8 @@
 import @1.2::DppProgressCode;
 import @1.2::DppFailureCode;
 
+import android.hardware.wifi@1.0::WifiChannelWidthInMhz;
+
 /**
  * OcspType: The type of OCSP request.
  */
@@ -53,13 +55,25 @@
 };
 
 /**
- * Connection Capabilities.
+ * Connection Capabilities supported by current network and device
  */
 struct ConnectionCapabilities {
     /**
      * Wifi Technology
      */
     WifiTechnology technology;
+    /**
+     * channel bandwidth
+     */
+    WifiChannelWidthInMhz channelBandwidth;
+    /**
+     * max number of Tx spatial streams
+     */
+    uint32_t maxNumberTxSpatialStreams;
+    /**
+     * max number of Rx spatial streams
+     */
+    uint32_t maxNumberRxSpatialStreams;
 };
 
 /**
diff --git a/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp
index 48b14f3..f7019d2 100644
--- a/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -207,6 +207,9 @@
         ISupplicantStaIfaceCallback::EapErrorCode /* eapErrorCode */) override {
         return Void();
     }
+    Return<void> onEapFailure_1_3(uint32_t /* eapErrorCode */) override {
+        return Void();
+    }
     Return<void> onWpsEventSuccess() override { return Void(); }
     Return<void> onWpsEventFail(
         const hidl_array<uint8_t, 6>& /* bssid */,
@@ -258,6 +261,12 @@
         const ISupplicantStaIfaceCallback::BssTmData& /* data */) override {
         return Void();
     }
+    Return<void> onStateChanged_1_3(
+        ISupplicantStaIfaceCallback::State /* newState */,
+        const hidl_array<uint8_t, 6>& /*bssid */, uint32_t /* id */,
+        const hidl_vec<uint8_t>& /* ssid */, bool /* filsHlpSent */) override {
+        return Void();
+    }
 };
 
 class IfacePmkCacheCallback : public IfaceCallback {
@@ -503,6 +512,83 @@
         EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
     });
 }
+
+/*
+ * FilsHlpAddRequest
+ */
+TEST_P(SupplicantStaIfaceHidlTest, FilsHlpAddRequest) {
+    uint32_t keyMgmtMask = 0;
+    uint8_t destMacAddr[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
+    std::vector<uint8_t> pktBuffer = {
+        0x08, 0x00, 0x45, 0x10, 0x01, 0x3a, 0x00, 0x00, 0x40, 0x00, 0x40, 0x11,
+        0x39, 0xa4, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x44,
+        0x00, 0x43, 0x01, 0x26, 0x77, 0x1e, 0x01, 0x01, 0x06, 0x00, 0x81, 0xf9,
+        0xf7, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xc3,
+        0x65, 0xca, 0x34, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x01, 0x3d,
+        0x07, 0x01, 0x86, 0xc3, 0x65, 0xca, 0x34, 0x63, 0x39, 0x02, 0x05, 0xdc,
+        0x3c, 0x0e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2d, 0x64, 0x68,
+        0x63, 0x70, 0x2d, 0x52, 0x37, 0x0a, 0x01, 0x03, 0x06, 0x0f, 0x1a, 0x1c,
+        0x33, 0x3a, 0x3b, 0x2b, 0xff, 0x00};
+
+    sta_iface_->getKeyMgmtCapabilities_1_3(
+        [&](const SupplicantStatus& status, uint32_t keyMgmtMaskInternal) {
+            EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+            keyMgmtMask = keyMgmtMaskInternal;
+        });
+
+    SupplicantStatusCode expectedStatusCode =
+        (keyMgmtMask & (ISupplicantStaNetwork::KeyMgmtMask::FILS_SHA256 |
+                        ISupplicantStaNetwork::KeyMgmtMask::FILS_SHA384))
+            ? SupplicantStatusCode::SUCCESS
+            : SupplicantStatusCode::FAILURE_UNKNOWN;
+
+    sta_iface_->filsHlpAddRequest(
+        destMacAddr, pktBuffer,
+        [expectedStatusCode](const SupplicantStatus& status) {
+            EXPECT_EQ(expectedStatusCode, status.code);
+        });
+}
+
+/*
+ * FilsHlpFlushRequest
+ */
+TEST_P(SupplicantStaIfaceHidlTest, FilsHlpFlushRequest) {
+    uint32_t keyMgmtMask = 0;
+    sta_iface_->getKeyMgmtCapabilities_1_3(
+        [&](const SupplicantStatus& status, uint32_t keyMgmtMaskInternal) {
+            EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+            keyMgmtMask = keyMgmtMaskInternal;
+        });
+
+    SupplicantStatusCode expectedStatusCode =
+        (keyMgmtMask & (ISupplicantStaNetwork::KeyMgmtMask::FILS_SHA256 |
+                        ISupplicantStaNetwork::KeyMgmtMask::FILS_SHA384))
+            ? SupplicantStatusCode::SUCCESS
+            : SupplicantStatusCode::FAILURE_UNKNOWN;
+
+    sta_iface_->filsHlpFlushRequest(
+        [expectedStatusCode](const SupplicantStatus& status) {
+            EXPECT_EQ(expectedStatusCode, status.code);
+        });
+}
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantStaIfaceHidlTest,
     testing::Combine(
diff --git a/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp b/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp
index d82db50..13f3366 100644
--- a/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp
+++ b/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp
@@ -285,6 +285,28 @@
             });
     }
 }
+/*
+ * SetEapErp
+ */
+TEST_P(SupplicantStaNetworkHidlTest, SetEapErp) {
+    uint32_t keyMgmtMask = 0;
+    sta_iface_->getKeyMgmtCapabilities_1_3(
+        [&](const SupplicantStatus &status, uint32_t keyMgmtMaskInternal) {
+            EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+            keyMgmtMask = keyMgmtMaskInternal;
+        });
+
+    SupplicantStatusCode expectedStatusCode =
+        (keyMgmtMask & (ISupplicantStaNetwork::KeyMgmtMask::FILS_SHA256 |
+                        ISupplicantStaNetwork::KeyMgmtMask::FILS_SHA384))
+            ? SupplicantStatusCode::SUCCESS
+            : SupplicantStatusCode::FAILURE_UNKNOWN;
+
+    sta_network_->setEapErp(
+        true, [expectedStatusCode](const SupplicantStatus &status) {
+            EXPECT_EQ(expectedStatusCode, status.code);
+        });
+}
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantStaNetworkHidlTest,
     testing::Combine(