Merge "-= src_available"
diff --git a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
index 2759801..222fad7 100644
--- a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
@@ -53,9 +53,9 @@
 
 TEST_P(AudioHidlDeviceTest, SetConnectedStateInvalidDeviceAddress) {
     doc::test("Check that invalid device address is rejected by IDevice::setConnectedState");
-    EXPECT_RESULT(Result::INVALID_ARGUMENTS,
+    EXPECT_RESULT(invalidArgsOrNotSupported,
                   getDevice()->setConnectedState(getInvalidDeviceAddress(), true));
-    EXPECT_RESULT(Result::INVALID_ARGUMENTS,
+    EXPECT_RESULT(invalidArgsOrNotSupported,
                   getDevice()->setConnectedState(getInvalidDeviceAddress(), false));
 }
 
diff --git a/audio/core/all-versions/vts/functional/VtsHalAudioV6_0TargetTest.xml b/audio/core/all-versions/vts/functional/VtsHalAudioV6_0TargetTest.xml
index f035baf..ae57125 100644
--- a/audio/core/all-versions/vts/functional/VtsHalAudioV6_0TargetTest.xml
+++ b/audio/core/all-versions/vts/functional/VtsHalAudioV6_0TargetTest.xml
@@ -34,5 +34,6 @@
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="VtsHalAudioV6_0TargetTest" />
+        <option name="native-test-timeout" value="5m" />
     </test>
 </configuration>
diff --git a/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml b/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml
index 6635f31..55dbaf1 100644
--- a/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml
+++ b/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml
@@ -34,5 +34,6 @@
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="VtsHalAudioV7_0TargetTest" />
+        <option name="native-test-timeout" value="5m" />
     </test>
 </configuration>
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecCapabilities.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecCapabilities.aidl
index e2a08a0..948c04a 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecCapabilities.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecCapabilities.aidl
@@ -37,11 +37,16 @@
   android.hardware.bluetooth.audio.CodecType codecType;
   android.hardware.bluetooth.audio.CodecCapabilities.Capabilities capabilities;
   @VintfStability
+  parcelable VendorCapabilities {
+    ParcelableHolder extension;
+  }
+  @VintfStability
   union Capabilities {
     android.hardware.bluetooth.audio.SbcCapabilities sbcCapabilities;
     android.hardware.bluetooth.audio.AacCapabilities aacCapabilities;
     android.hardware.bluetooth.audio.LdacCapabilities ldacCapabilities;
     android.hardware.bluetooth.audio.AptxCapabilities aptxCapabilities;
     android.hardware.bluetooth.audio.Lc3Capabilities lc3Capabilities;
+    android.hardware.bluetooth.audio.CodecCapabilities.VendorCapabilities vendorCapabilities;
   }
 }
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecConfiguration.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecConfiguration.aidl
index 34ebd60..32bccd8 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecConfiguration.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecConfiguration.aidl
@@ -40,11 +40,18 @@
   boolean isScmstEnabled;
   android.hardware.bluetooth.audio.CodecConfiguration.CodecSpecific config;
   @VintfStability
+  parcelable VendorConfiguration {
+    int vendorId;
+    char codecId;
+    ParcelableHolder codecConfig;
+  }
+  @VintfStability
   union CodecSpecific {
     android.hardware.bluetooth.audio.SbcConfiguration sbcConfig;
     android.hardware.bluetooth.audio.AacConfiguration aacConfig;
     android.hardware.bluetooth.audio.LdacConfiguration ldacConfig;
     android.hardware.bluetooth.audio.AptxConfiguration aptxConfig;
     android.hardware.bluetooth.audio.Lc3Configuration lc3Config;
+    android.hardware.bluetooth.audio.CodecConfiguration.VendorConfiguration vendorConfig;
   }
 }
diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecCapabilities.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecCapabilities.aidl
index 5bf0252..41e2431 100644
--- a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecCapabilities.aidl
+++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecCapabilities.aidl
@@ -30,12 +30,17 @@
 @VintfStability
 parcelable CodecCapabilities {
     @VintfStability
+    parcelable VendorCapabilities {
+        ParcelableHolder extension;
+    }
+    @VintfStability
     union Capabilities {
         SbcCapabilities sbcCapabilities;
         AacCapabilities aacCapabilities;
         LdacCapabilities ldacCapabilities;
         AptxCapabilities aptxCapabilities;
         Lc3Capabilities lc3Capabilities;
+        VendorCapabilities vendorCapabilities;
     }
     CodecType codecType;
     Capabilities capabilities;
diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecConfiguration.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecConfiguration.aidl
index 9e43f22..3679537 100644
--- a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecConfiguration.aidl
+++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecConfiguration.aidl
@@ -30,12 +30,19 @@
 @VintfStability
 parcelable CodecConfiguration {
     @VintfStability
+    parcelable VendorConfiguration {
+        int vendorId;
+        char codecId;
+        ParcelableHolder codecConfig;
+    }
+    @VintfStability
     union CodecSpecific {
         SbcConfiguration sbcConfig;
         AacConfiguration aacConfig;
         LdacConfiguration ldacConfig;
         AptxConfiguration aptxConfig;
         Lc3Configuration lc3Config;
+        VendorConfiguration vendorConfig;
     }
     CodecType codecType;
     /**
diff --git a/bluetooth/audio/aidl/default/Android.bp b/bluetooth/audio/aidl/default/Android.bp
index 846702f..fc882d4 100644
--- a/bluetooth/audio/aidl/default/Android.bp
+++ b/bluetooth/audio/aidl/default/Android.bp
@@ -8,7 +8,7 @@
 }
 
 cc_library_shared {
-    name: "android.hardware.bluetooth.audio-V1-impl",
+    name: "android.hardware.bluetooth.audio-impl",
     vendor: true,
     vintf_fragments: ["bluetooth_audio.xml"],
     srcs: [
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
index 380732f..516ebe8 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
@@ -78,7 +78,7 @@
     .bitsPerSample = {24},
 };
 
-static const Lc3Capabilities kDefaultOffloadLc3Capability = {
+static const Lc3Capabilities kDefaultA2dpOffloadLc3Capability = {
     .samplingFrequencyHz = {44100, 48000},
     .frameDurationUs = {7500, 10000},
     .channelMode = {ChannelMode::MONO, ChannelMode::STEREO},
@@ -285,11 +285,11 @@
   const Lc3Configuration lc3_data =
       codec_specific.get<CodecConfiguration::CodecSpecific::lc3Config>();
 
-  if (ContainedInVector(kDefaultOffloadLc3Capability.samplingFrequencyHz,
+  if (ContainedInVector(kDefaultA2dpOffloadLc3Capability.samplingFrequencyHz,
                         lc3_data.samplingFrequencyHz) &&
-      ContainedInVector(kDefaultOffloadLc3Capability.frameDurationUs,
+      ContainedInVector(kDefaultA2dpOffloadLc3Capability.frameDurationUs,
                         lc3_data.frameDurationUs) &&
-      ContainedInVector(kDefaultOffloadLc3Capability.channelMode,
+      ContainedInVector(kDefaultA2dpOffloadLc3Capability.channelMode,
                         lc3_data.channelMode)) {
     return true;
   }
@@ -352,11 +352,10 @@
       case CodecType::LC3:
         codec_capability.capabilities
             .set<CodecCapabilities::Capabilities::lc3Capabilities>(
-                kDefaultOffloadLc3Capability);
+                kDefaultA2dpOffloadLc3Capability);
         break;
       case CodecType::UNKNOWN:
       case CodecType::VENDOR:
-        codec_capability = {};
         break;
     }
   }
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.h b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.h
index c542ce5..0259a7e 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.h
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.h
@@ -45,11 +45,7 @@
       const SessionType& session_type, const CodecConfiguration& codec_config);
 
   static bool IsOffloadLeAudioConfigurationValid(
-      const SessionType& session_type, const Lc3Configuration& codec_config);
-
-  static bool IsOffloadLeAudioConfigurationValid(
-      const SessionType& session_type,
-      const LeAudioConfiguration& codec_config);
+      const SessionType& session_type, const LeAudioConfiguration&);
 
   static std::vector<LeAudioCodecCapabilitiesSetting>
   GetLeAudioOffloadCodecCapabilities(const SessionType& session_type);
@@ -77,8 +73,6 @@
       const CodecConfiguration::CodecSpecific& codec_specific);
   static bool IsOffloadLc3ConfigurationValid(
       const CodecConfiguration::CodecSpecific& codec_specific);
-  static bool IsOffloadLeAudioConfigurationValid(
-      const SessionType& session_type, const LeAudioCodecConfiguration&);
 };
 
 }  // namespace audio
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
index 8052b01..f626db8 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
@@ -35,22 +35,8 @@
 static constexpr int kWritePollMs = 1;  // polled non-blocking interval
 static constexpr int kReadPollMs = 1;   // polled non-blocking interval
 
-const CodecConfiguration BluetoothAudioSession::kInvalidCodecConfiguration = {};
-const LeAudioConfiguration kInvalidLeAudioConfiguration = {};
-AudioConfiguration BluetoothAudioSession::invalidSoftwareAudioConfiguration =
-    {};
-AudioConfiguration BluetoothAudioSession::invalidOffloadAudioConfiguration = {};
-AudioConfiguration BluetoothAudioSession::invalidLeOffloadAudioConfig = {};
-
 BluetoothAudioSession::BluetoothAudioSession(const SessionType& session_type)
-    : session_type_(session_type), stack_iface_(nullptr), data_mq_(nullptr) {
-  invalidSoftwareAudioConfiguration.set<AudioConfiguration::pcmConfig>(
-      kInvalidPcmConfiguration);
-  invalidOffloadAudioConfiguration.set<AudioConfiguration::a2dpConfig>(
-      kInvalidCodecConfiguration);
-  invalidLeOffloadAudioConfig.set<AudioConfiguration::leAudioConfig>(
-      kInvalidLeAudioConfiguration);
-}
+    : session_type_(session_type), stack_iface_(nullptr), data_mq_(nullptr) {}
 
 /***
  *
@@ -72,13 +58,7 @@
   } else if (!UpdateDataPath(mq_desc)) {
     LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
                << " MqDescriptor Invalid";
-    if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
-      audio_config_ = std::make_unique<AudioConfiguration>(
-          invalidOffloadAudioConfiguration);
-    } else {
-      audio_config_ = std::make_unique<AudioConfiguration>(
-          invalidSoftwareAudioConfiguration);
-    }
+    audio_config_ = nullptr;
   } else {
     stack_iface_ = stack_iface;
     LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
@@ -91,13 +71,7 @@
   std::lock_guard<std::recursive_mutex> guard(mutex_);
   bool toggled = IsSessionReady();
   LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
-  if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
-    audio_config_ =
-        std::make_unique<AudioConfiguration>(invalidOffloadAudioConfiguration);
-  } else {
-    audio_config_ =
-        std::make_unique<AudioConfiguration>(invalidSoftwareAudioConfiguration);
-  }
+  audio_config_ = nullptr;
   stack_iface_ = nullptr;
   UpdateDataPath(nullptr);
   if (toggled) {
@@ -111,17 +85,17 @@
  *
  ***/
 
-const AudioConfiguration& BluetoothAudioSession::GetAudioConfig() {
+const AudioConfiguration BluetoothAudioSession::GetAudioConfig() {
   std::lock_guard<std::recursive_mutex> guard(mutex_);
   if (!IsSessionReady()) {
     switch (session_type_) {
       case SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
-        return invalidOffloadAudioConfiguration;
+        return AudioConfiguration(CodecConfiguration{});
       case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
       case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
-        return invalidLeOffloadAudioConfig;
+        return AudioConfiguration(LeAudioConfiguration{});
       default:
-        return invalidSoftwareAudioConfiguration;
+        return AudioConfiguration(PcmConfiguration{});
     }
   }
   return *audio_config_;
@@ -164,7 +138,7 @@
        session_type_ ==
            SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
        (data_mq_ != nullptr && data_mq_->isValid()));
-  return stack_iface_ != nullptr && is_mq_valid;
+  return stack_iface_ != nullptr && is_mq_valid && audio_config_ != nullptr;
 }
 
 /***
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h
index 85fd571..73bc0f8 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h
@@ -144,7 +144,7 @@
    * The control function is for the bluetooth_audio module to get the current
    * AudioConfiguration
    ***/
-  const AudioConfiguration& GetAudioConfig();
+  const AudioConfiguration GetAudioConfig();
 
   /***
    * The report function is used to report that the Bluetooth stack has notified
@@ -173,14 +173,6 @@
   // Return if IBluetoothAudioProviderFactory implementation existed
   static bool IsAidlAvailable();
 
-  static constexpr PcmConfiguration kInvalidPcmConfiguration = {};
-  // can't be constexpr because of non-literal type
-  static const CodecConfiguration kInvalidCodecConfiguration;
-
-  static AudioConfiguration invalidSoftwareAudioConfiguration;
-  static AudioConfiguration invalidOffloadAudioConfiguration;
-  static AudioConfiguration invalidLeOffloadAudioConfig;
-
  private:
   // using recursive_mutex to allow hwbinder to re-enter again.
   std::recursive_mutex mutex_;
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h b/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h
index a3ed428..aff01e5 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h
@@ -79,11 +79,15 @@
         BluetoothAudioSessionInstance::GetSessionInstance(session_type);
     if (session_ptr != nullptr) {
       return session_ptr->GetAudioConfig();
-    } else if (session_type ==
-               SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
-      return BluetoothAudioSession::invalidOffloadAudioConfiguration;
-    } else {
-      return BluetoothAudioSession::invalidSoftwareAudioConfiguration;
+    }
+    switch (session_type) {
+      case SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
+        return AudioConfiguration(CodecConfiguration{});
+      case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
+      case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
+        return AudioConfiguration(LeAudioConfiguration{});
+      default:
+        return AudioConfiguration(PcmConfiguration{});
     }
   }
 
diff --git a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp
index 284d0bb..632a389 100644
--- a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp
+++ b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp
@@ -93,16 +93,6 @@
     std::unordered_map<uint16_t, std::shared_ptr<PortStatusCallbacks_2_2>>>
     legacy_callback_table;
 
-const static std::unordered_map<SessionType_2_0, SessionType>
-    session_type_2_0_to_aidl_map{
-        {SessionType_2_0::A2DP_SOFTWARE_ENCODING_DATAPATH,
-         SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH},
-        {SessionType_2_0::A2DP_HARDWARE_OFFLOAD_DATAPATH,
-         SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH},
-        {SessionType_2_0::HEARING_AID_SOFTWARE_ENCODING_DATAPATH,
-         SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH},
-    };
-
 const static std::unordered_map<SessionType_2_1, SessionType>
     session_type_2_1_to_aidl_map{
         {SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH,
@@ -121,18 +111,6 @@
          SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH},
     };
 
-const static std::unordered_map<int32_t, SampleRate_2_0>
-    sample_rate_to_hidl_2_0_map{
-        {44100, SampleRate_2_0::RATE_44100},
-        {48000, SampleRate_2_0::RATE_48000},
-        {88200, SampleRate_2_0::RATE_88200},
-        {96000, SampleRate_2_0::RATE_96000},
-        {176400, SampleRate_2_0::RATE_176400},
-        {192000, SampleRate_2_0::RATE_192000},
-        {16000, SampleRate_2_0::RATE_16000},
-        {24000, SampleRate_2_0::RATE_24000},
-    };
-
 const static std::unordered_map<int32_t, SampleRate_2_1>
     sample_rate_to_hidl_2_1_map{
         {44100, SampleRate_2_1::RATE_44100},
@@ -211,20 +189,6 @@
         {LdacQualityIndex::ABR, LdacQualityIndex_2_0::QUALITY_ABR},
     };
 
-const static std::unordered_map<LeAudioMode, LeAudioMode_2_2>
-    leaudio_mode_to_hidl_map{
-        {LeAudioMode::UNKNOWN, LeAudioMode_2_2::UNKNOWN},
-        {LeAudioMode::UNICAST, LeAudioMode_2_2::UNICAST},
-        {LeAudioMode::BROADCAST, LeAudioMode_2_2::BROADCAST},
-    };
-
-inline SessionType from_session_type_2_0(
-    const SessionType_2_0& session_type_hidl) {
-  auto it = session_type_2_0_to_aidl_map.find(session_type_hidl);
-  if (it != session_type_2_0_to_aidl_map.end()) return it->second;
-  return SessionType::UNKNOWN;
-}
-
 inline SessionType from_session_type_2_1(
     const SessionType_2_1& session_type_hidl) {
   auto it = session_type_2_1_to_aidl_map.find(session_type_hidl);
@@ -232,6 +196,11 @@
   return SessionType::UNKNOWN;
 }
 
+inline SessionType from_session_type_2_0(
+    const SessionType_2_0& session_type_hidl) {
+  return from_session_type_2_1(static_cast<SessionType_2_1>(session_type_hidl));
+}
+
 inline HidlStatus to_hidl_status(const BluetoothAudioStatus& status) {
   switch (status) {
     case BluetoothAudioStatus::SUCCESS:
@@ -243,18 +212,19 @@
   }
 }
 
-inline SampleRate_2_0 to_hidl_sample_rate_2_0(const int32_t sample_rate_hz) {
-  auto it = sample_rate_to_hidl_2_0_map.find(sample_rate_hz);
-  if (it != sample_rate_to_hidl_2_0_map.end()) return it->second;
-  return SampleRate_2_0::RATE_UNKNOWN;
-}
-
 inline SampleRate_2_1 to_hidl_sample_rate_2_1(const int32_t sample_rate_hz) {
   auto it = sample_rate_to_hidl_2_1_map.find(sample_rate_hz);
   if (it != sample_rate_to_hidl_2_1_map.end()) return it->second;
   return SampleRate_2_1::RATE_UNKNOWN;
 }
 
+inline SampleRate_2_0 to_hidl_sample_rate_2_0(const int32_t sample_rate_hz) {
+  auto it = sample_rate_to_hidl_2_1_map.find(sample_rate_hz);
+  if (it != sample_rate_to_hidl_2_1_map.end())
+    return static_cast<SampleRate_2_0>(it->second);
+  return SampleRate_2_0::RATE_UNKNOWN;
+}
+
 inline BitsPerSample_2_0 to_hidl_bits_per_sample(const int8_t bit_per_sample) {
   switch (bit_per_sample) {
     case 16:
@@ -449,18 +419,49 @@
 
 inline Lc3CodecConfig_2_1 to_hidl_leaudio_config_2_1(
     const LeAudioConfiguration& leaudio_config) {
-  auto& unicast_config =
-      leaudio_config.modeConfig
-          .get<LeAudioConfiguration::LeAudioModeConfig::unicastConfig>();
+  Lc3CodecConfig_2_1 hidl_lc3_codec_config = {
+      .audioChannelAllocation = 0,
+  };
+  if (leaudio_config.modeConfig.getTag() ==
+      LeAudioConfiguration::LeAudioModeConfig::unicastConfig) {
+    auto& unicast_config =
+        leaudio_config.modeConfig
+            .get<LeAudioConfiguration::LeAudioModeConfig::unicastConfig>();
+    if (unicast_config.leAudioCodecConfig.getTag() ==
+        LeAudioCodecConfiguration::lc3Config) {
+      LOG(FATAL) << __func__ << ": unexpected codec type(vendor?)";
+    }
+    auto& le_codec_config = unicast_config.leAudioCodecConfig
+                                .get<LeAudioCodecConfiguration::lc3Config>();
 
-  auto& le_codec_config = unicast_config.leAudioCodecConfig
-                              .get<LeAudioCodecConfiguration::lc3Config>();
+    hidl_lc3_codec_config.lc3Config = to_hidl_lc3_config_2_1(le_codec_config);
 
-  Lc3CodecConfig_2_1 hidl_lc3_codec_config;
-  hidl_lc3_codec_config.lc3Config = to_hidl_lc3_config_2_1(le_codec_config);
+    for (const auto& map : unicast_config.streamMap) {
+      hidl_lc3_codec_config.audioChannelAllocation |=
+          map.audioChannelAllocation;
+    }
+  } else {
+    // NOTE: Broadcast is not officially supported in HIDL
+    auto& bcast_config =
+        leaudio_config.modeConfig
+            .get<LeAudioConfiguration::LeAudioModeConfig::broadcastConfig>();
+    if (bcast_config.streamMap.empty()) {
+      return hidl_lc3_codec_config;
+    }
+    if (bcast_config.streamMap[0].leAudioCodecConfig.getTag() !=
+        LeAudioCodecConfiguration::lc3Config) {
+      LOG(FATAL) << __func__ << ": unexpected codec type(vendor?)";
+    }
+    auto& le_codec_config =
+        bcast_config.streamMap[0]
+            .leAudioCodecConfig.get<LeAudioCodecConfiguration::lc3Config>();
+    hidl_lc3_codec_config.lc3Config = to_hidl_lc3_config_2_1(le_codec_config);
 
-  hidl_lc3_codec_config.audioChannelAllocation =
-      unicast_config.streamMap.size();
+    for (const auto& map : bcast_config.streamMap) {
+      hidl_lc3_codec_config.audioChannelAllocation |=
+          map.audioChannelAllocation;
+    }
+  }
 
   return hidl_lc3_codec_config;
 }
@@ -468,13 +469,10 @@
 inline LeAudioConfig_2_2 to_hidl_leaudio_config_2_2(
     const LeAudioConfiguration& leaudio_config) {
   LeAudioConfig_2_2 hidl_leaudio_config;
-  if (leaudio_mode_to_hidl_map.find(leaudio_config.mode) !=
-      leaudio_mode_to_hidl_map.end()) {
-    hidl_leaudio_config.mode = leaudio_mode_to_hidl_map.at(leaudio_config.mode);
-  }
 
   if (leaudio_config.modeConfig.getTag() ==
       LeAudioConfiguration::LeAudioModeConfig::unicastConfig) {
+    hidl_leaudio_config.mode = LeAudioMode_2_2::UNICAST;
     auto& unicast_config =
         leaudio_config.modeConfig
             .get<LeAudioConfiguration::LeAudioModeConfig::unicastConfig>();
@@ -497,6 +495,7 @@
     }
   } else if (leaudio_config.modeConfig.getTag() ==
              LeAudioConfiguration::LeAudioModeConfig::broadcastConfig) {
+    hidl_leaudio_config.mode = LeAudioMode_2_2::BROADCAST;
     auto bcast_config =
         leaudio_config.modeConfig
             .get<LeAudioConfiguration::LeAudioModeConfig::broadcastConfig>();
@@ -641,6 +640,12 @@
       from_session_type_2_0(session_type), buffer, bytes);
 }
 
+size_t HidlToAidlMiddleware_2_0::InReadPcmData(
+    const SessionType_2_0& session_type, void* buffer, size_t bytes) {
+  return BluetoothAudioSessionControl::InReadPcmData(
+      from_session_type_2_0(session_type), buffer, bytes);
+}
+
 bool HidlToAidlMiddleware_2_0::IsAidlAvailable() {
   return BluetoothAudioSession::IsAidlAvailable();
 }
diff --git a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_0.h b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_0.h
index d10ee37..b124d8f 100644
--- a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_0.h
+++ b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_0.h
@@ -64,6 +64,9 @@
 
   static size_t OutWritePcmData(const SessionType_2_0& session_type,
                                 const void* buffer, size_t bytes);
+
+  static size_t InReadPcmData(const SessionType_2_0& session_type, void* buffer,
+                              size_t bytes);
 };
 
 }  // namespace audio
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession.cpp b/bluetooth/audio/utils/session/BluetoothAudioSession.cpp
index 6d5608b..283952e 100644
--- a/bluetooth/audio/utils/session/BluetoothAudioSession.cpp
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession.cpp
@@ -437,6 +437,9 @@
 
 // The control function reads stream from FMQ
 size_t BluetoothAudioSession::InReadPcmData(void* buffer, size_t bytes) {
+  if (HidlToAidlMiddleware_2_0::IsAidlAvailable())
+    return HidlToAidlMiddleware_2_0::InReadPcmData(session_type_, buffer,
+                                                   bytes);
   if (buffer == nullptr || !bytes) return 0;
   size_t totalRead = 0;
   int ms_timeout = kFmqReceiveTimeoutMs;
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 2d8a82e..f751efc 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -425,6 +425,13 @@
             <instance>default</instance>
         </interface>
     </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.nfc</name>
+        <interface>
+            <name>INfc</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.oemlock</name>
         <version>1.0</version>
diff --git a/graphics/common/OWNERS b/graphics/common/OWNERS
new file mode 100644
index 0000000..94999ea
--- /dev/null
+++ b/graphics/common/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 1075130
+adyabr@google.com
+alecmouri@google.com
+jreck@google.com
+scroggo@google.com
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index b071f71..fa294ff 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -161,7 +161,8 @@
         return mGralloc->allocate(
                 width, height, /*layerCount*/ 1,
                 static_cast<common::V1_1::PixelFormat>(PixelFormat::RGBA_8888),
-                static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN));
+                static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY));
     }
 
     struct TestParameters {
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/HealthInfo.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/HealthInfo.aidl
index 34a87a6..97d9e84 100644
--- a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/HealthInfo.aidl
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/HealthInfo.aidl
@@ -37,6 +37,7 @@
   boolean chargerAcOnline;
   boolean chargerUsbOnline;
   boolean chargerWirelessOnline;
+  boolean chargerDockOnline;
   int maxChargingCurrentMicroamps;
   int maxChargingVoltageMicrovolts;
   android.hardware.health.BatteryStatus batteryStatus;
diff --git a/health/aidl/android/hardware/health/HealthInfo.aidl b/health/aidl/android/hardware/health/HealthInfo.aidl
index 504e218..5b98baf 100644
--- a/health/aidl/android/hardware/health/HealthInfo.aidl
+++ b/health/aidl/android/hardware/health/HealthInfo.aidl
@@ -40,6 +40,10 @@
      */
     boolean chargerWirelessOnline;
     /**
+     * Dock charger state - 'true' if online
+     */
+    boolean chargerDockOnline;
+    /**
      * Maximum charging current supported by charger in µA
      */
     int maxChargingCurrentMicroamps;
diff --git a/health/aidl/default/HalHealthLoop.cpp b/health/aidl/default/HalHealthLoop.cpp
index c9a081e..ec23c10 100644
--- a/health/aidl/default/HalHealthLoop.cpp
+++ b/health/aidl/default/HalHealthLoop.cpp
@@ -61,7 +61,7 @@
 
 void HalHealthLoop::set_charger_online(const HealthInfo& health_info) {
     charger_online_ = health_info.chargerAcOnline || health_info.chargerUsbOnline ||
-                      health_info.chargerWirelessOnline;
+                      health_info.chargerWirelessOnline || health_info.chargerDockOnline;
 }
 
 }  // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/health-convert.cpp b/health/aidl/default/health-convert.cpp
index b5251f4..6118865 100644
--- a/health/aidl/default/health-convert.cpp
+++ b/health/aidl/default/health-convert.cpp
@@ -22,6 +22,7 @@
     p->chargerAcOnline = info.chargerAcOnline;
     p->chargerUsbOnline = info.chargerUsbOnline;
     p->chargerWirelessOnline = info.chargerWirelessOnline;
+    p->chargerDockOnline = info.chargerDockOnline;
     p->maxChargingCurrent = info.maxChargingCurrentMicroamps;
     p->maxChargingVoltage = info.maxChargingVoltageMicrovolts;
     p->batteryStatus = static_cast<int>(info.batteryStatus);
diff --git a/neuralnetworks/aidl/vts/functional/Android.bp b/neuralnetworks/aidl/vts/functional/Android.bp
index 1ed15b8..356cdb0 100644
--- a/neuralnetworks/aidl/vts/functional/Android.bp
+++ b/neuralnetworks/aidl/vts/functional/Android.bp
@@ -30,6 +30,7 @@
         "neuralnetworks_vts_functional_defaults",
         "use_libaidlvintf_gtest_helper_static",
     ],
+    host_supported: true,
     srcs: [
         "BasicTests.cpp",
         "Callbacks.cpp",
@@ -46,18 +47,11 @@
     ],
     shared_libs: [
         "libbinder_ndk",
-        "libnativewindow",
-        "libvndksupport",
     ],
     static_libs: [
-        "android.hidl.allocator@1.0",
-        "android.hidl.memory@1.0",
         "libaidlcommonsupport",
-        "libgmock",
-        "libhidlmemory",
         "libneuralnetworks_common",
         "libneuralnetworks_generated_test_harness",
-        "libsync",
     ],
     whole_static_libs: [
         "neuralnetworks_generated_AIDL_V3_example",
@@ -73,6 +67,34 @@
     ],
     test_suites: [
         "general-tests",
-        "vts",
     ],
+    target: {
+        android: {
+            shared_libs: [
+                "libnativewindow",
+                "libvndksupport",
+            ],
+            static_libs: [
+                "libsync",
+            ],
+            test_suites: [
+                "vts",
+            ],
+            test_config: "AndroidTestDevice.xml",
+        },
+        host: {
+            shared_libs: [
+                "libtextclassifier_hash",
+            ],
+            static_libs: [
+                "neuralnetworks_canonical_sample_driver",
+                "neuralnetworks_utils_hal_adapter_aidl",
+            ],
+            exclude_static_libs: [
+                "VtsHalHidlTestUtils",
+                "libaidlvintf_gtest_helper",
+            ],
+            test_config: "AndroidTestHost.xml",
+        },
+    },
 }
diff --git a/neuralnetworks/aidl/vts/functional/AndroidTest.xml b/neuralnetworks/aidl/vts/functional/AndroidTestDevice.xml
similarity index 100%
rename from neuralnetworks/aidl/vts/functional/AndroidTest.xml
rename to neuralnetworks/aidl/vts/functional/AndroidTestDevice.xml
diff --git a/neuralnetworks/aidl/vts/functional/AndroidTestHost.xml b/neuralnetworks/aidl/vts/functional/AndroidTestHost.xml
new file mode 100644
index 0000000..7372a31
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/AndroidTestHost.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs VtsHalNeuralnetworksTargetTest.">
+    <test class="com.android.tradefed.testtype.HostGTest" >
+        <option name="module-name" value="VtsHalNeuralnetworksTargetTest" />
+        <option name="native-test-timeout" value="15m" />
+    </test>
+</configuration>
+
diff --git a/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp
index 77208aa..7451f7e 100644
--- a/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp
@@ -23,7 +23,6 @@
 #include <fcntl.h>
 #include <ftw.h>
 #include <gtest/gtest.h>
-#include <hidlmemory/mapping.h>
 #include <unistd.h>
 
 #include <cstdio>
@@ -34,7 +33,6 @@
 
 #include "Callbacks.h"
 #include "GeneratedTestHarness.h"
-#include "MemoryUtils.h"
 #include "TestHarness.h"
 #include "Utils.h"
 #include "VtsHalNeuralnetworks.h"
@@ -229,7 +227,11 @@
 
         // Create cache directory. The cache directory and a temporary cache file is always created
         // to test the behavior of prepareModelFromCache, even when caching is not supported.
+#ifdef __ANDROID__
         char cacheDirTemp[] = "/data/local/tmp/TestCompilationCachingXXXXXX";
+#else   // __ANDROID__
+        char cacheDirTemp[] = "/tmp/TestCompilationCachingXXXXXX";
+#endif  // __ANDROID__
         char* cacheDir = mkdtemp(cacheDirTemp);
         ASSERT_NE(cacheDir, nullptr);
         mCacheDir = cacheDir;
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
index 8c8a87a..40f6cd1 100644
--- a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
@@ -20,7 +20,6 @@
 #include <aidl/android/hardware/neuralnetworks/RequestMemoryPool.h>
 #include <android-base/logging.h>
 #include <android/binder_auto_utils.h>
-#include <android/sync.h>
 #include <gtest/gtest.h>
 
 #include <algorithm>
@@ -30,7 +29,6 @@
 #include <numeric>
 #include <vector>
 
-#include <MemoryUtils.h>
 #include <android/binder_status.h>
 #include <nnapi/Result.h>
 #include <nnapi/SharedMemory.h>
@@ -43,6 +41,10 @@
 #include "Utils.h"
 #include "VtsHalNeuralnetworks.h"
 
+#ifdef __ANDROID__
+#include <android/sync.h>
+#endif  // __ANDROID__
+
 namespace aidl::android::hardware::neuralnetworks::vts::functional {
 
 namespace nn = ::android::nn;
@@ -281,10 +283,14 @@
 }  // namespace
 
 void waitForSyncFence(int syncFd) {
-    constexpr int kInfiniteTimeout = -1;
     ASSERT_GT(syncFd, 0);
+#ifdef __ANDROID__
+    constexpr int kInfiniteTimeout = -1;
     int r = sync_wait(syncFd, kInfiniteTimeout);
     ASSERT_GE(r, 0);
+#else   // __ANDROID__
+    LOG(FATAL) << "waitForSyncFence not supported on host";
+#endif  // __ANDROID__
 }
 
 Model createModel(const TestModel& testModel) {
@@ -895,7 +901,11 @@
             outputTypesList = {OutputType::FULLY_SPECIFIED};
             measureTimingList = {false};
             executorList = {Executor::SYNC, Executor::BURST, Executor::FENCED};
+#ifdef __ANDROID__
             memoryTypeList = {MemoryType::BLOB_AHWB, MemoryType::DEVICE};
+#else   // __ANDROID__
+            memoryTypeList = {MemoryType::DEVICE};  // BLOB_AHWB is not supported on the host.
+#endif  // __ANDROID__
         } break;
         case TestKind::FENCED_COMPUTE: {
             outputTypesList = {OutputType::FULLY_SPECIFIED};
diff --git a/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp b/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
index 97760ae..f8341b1 100644
--- a/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
+++ b/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "neuralnetworks_aidl_hal_test"
 
 #include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <aidl/android/hardware/neuralnetworks/IPreparedModel.h>
 #include <android-base/logging.h>
 #include <android/binder_auto_utils.h>
 #include <android/binder_interface_utils.h>
@@ -33,7 +34,6 @@
 
 #include "Callbacks.h"
 #include "GeneratedTestHarness.h"
-#include "MemoryUtils.h"
 #include "Utils.h"
 #include "VtsHalNeuralnetworks.h"
 
@@ -191,7 +191,7 @@
 }
 
 // A placeholder invalid IPreparedModel class for MemoryDomainAllocateTest.InvalidPreparedModel
-class InvalidPreparedModel : public BnPreparedModel {
+class InvalidPreparedModel final : public IPreparedModel {
   public:
     ndk::ScopedAStatus executeSynchronously(const Request&, bool, int64_t, int64_t,
                                             ExecutionResult*) override {
@@ -225,6 +225,16 @@
         return ndk::ScopedAStatus::fromServiceSpecificError(
                 static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
     }
+    ndk::ScopedAStatus getInterfaceVersion(int32_t* /*interfaceVersion*/) {
+        return ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
+    }
+    ndk::ScopedAStatus getInterfaceHash(std::string* /*interfaceHash*/) {
+        return ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
+    }
+    ndk::SpAIBinder asBinder() override { return ::ndk::SpAIBinder{}; }
+    bool isRemote() override { return true; }
 };
 
 template <typename... Args>
diff --git a/neuralnetworks/aidl/vts/functional/Utils.cpp b/neuralnetworks/aidl/vts/functional/Utils.cpp
index efd5bca..1bc76f2 100644
--- a/neuralnetworks/aidl/vts/functional/Utils.cpp
+++ b/neuralnetworks/aidl/vts/functional/Utils.cpp
@@ -21,18 +21,20 @@
 #include <aidl/android/hardware/neuralnetworks/OperandType.h>
 #include <android-base/logging.h>
 #include <android/binder_status.h>
-#include <android/hardware_buffer.h>
 
 #include <sys/mman.h>
 #include <iostream>
 #include <limits>
 #include <numeric>
 
-#include <MemoryUtils.h>
 #include <nnapi/SharedMemory.h>
 #include <nnapi/hal/aidl/Conversions.h>
 #include <nnapi/hal/aidl/Utils.h>
 
+#ifdef __ANDROID__
+#include <android/hardware_buffer.h>
+#endif  // __ANDROID__
+
 namespace aidl::android::hardware::neuralnetworks {
 
 using test_helper::TestBuffer;
@@ -140,7 +142,8 @@
     return ahwb->mIsValid ? std::move(ahwb) : nullptr;
 }
 
-void TestBlobAHWB::initialize(uint32_t size) {
+void TestBlobAHWB::initialize([[maybe_unused]] uint32_t size) {
+#ifdef __ANDROID__
     mIsValid = false;
     ASSERT_GT(size, 0);
     const auto usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
@@ -164,6 +167,9 @@
     mAidlMemory = utils::convert(mMemory).value();
 
     mIsValid = true;
+#else   // __ANDROID__
+    LOG(FATAL) << "TestBlobAHWB::initialize not supported on host";
+#endif  // __ANDROID__
 }
 
 std::string gtestCompliantName(std::string name) {
diff --git a/neuralnetworks/aidl/vts/functional/Utils.h b/neuralnetworks/aidl/vts/functional/Utils.h
index 0db3f8c..4e0a4aa 100644
--- a/neuralnetworks/aidl/vts/functional/Utils.h
+++ b/neuralnetworks/aidl/vts/functional/Utils.h
@@ -18,7 +18,6 @@
 #define ANDROID_HARDWARE_NEURALNETWORKS_AIDL_UTILS_H
 
 #include <android-base/logging.h>
-#include <android/hardware_buffer.h>
 #include <gtest/gtest.h>
 
 #include <algorithm>
diff --git a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp
index ad93e6d..51b4805 100644
--- a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp
@@ -15,6 +15,7 @@
  */
 
 #define LOG_TAG "neuralnetworks_aidl_hal_test"
+
 #include "VtsHalNeuralnetworks.h"
 
 #include <android-base/logging.h>
@@ -28,13 +29,19 @@
 #include <utility>
 
 #include <TestHarness.h>
-#include <aidl/Vintf.h>
 #include <nnapi/hal/aidl/Conversions.h>
 
 #include "Callbacks.h"
 #include "GeneratedTestHarness.h"
 #include "Utils.h"
 
+#ifdef __ANDROID__
+#include <aidl/Vintf.h>
+#else  // __ANDROID__
+#include <CanonicalDevice.h>
+#include <nnapi/hal/aidl/Adapter.h>
+#endif  // __ANDROID__
+
 namespace aidl::android::hardware::neuralnetworks::vts::functional {
 
 using implementation::PreparedModelCallback;
@@ -111,6 +118,7 @@
     ASSERT_TRUE(deviceIsResponsive);
 }
 
+#ifdef __ANDROID__
 static NamedDevice makeNamedDevice(const std::string& name) {
     ndk::SpAIBinder binder(AServiceManager_waitForService(name.c_str()));
     return {name, IDevice::fromBinder(binder)};
@@ -127,6 +135,14 @@
     std::transform(names.begin(), names.end(), std::back_inserter(namedDevices), makeNamedDevice);
     return namedDevices;
 }
+#else   // __ANDROID__
+static std::vector<NamedDevice> getNamedDevicesImpl() {
+    const std::string name = "nnapi-sample";
+    auto device = std::make_shared<const ::android::nn::sample::Device>(name);
+    auto aidlDevice = adapter::adapt(device);
+    return {{name, aidlDevice}};
+}
+#endif  // __ANDROID__
 
 const std::vector<NamedDevice>& getNamedDevices() {
     const static std::vector<NamedDevice> devices = getNamedDevicesImpl();
diff --git a/nfc/OWNERS b/nfc/OWNERS
new file mode 100644
index 0000000..7867204
--- /dev/null
+++ b/nfc/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 48448
+alisher@google.com
+georgekgchang@google.com
+jackcwyu@google.com
+
+
diff --git a/nfc/aidl/Android.bp b/nfc/aidl/Android.bp
new file mode 100644
index 0000000..d390c7e
--- /dev/null
+++ b/nfc/aidl/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+aidl_interface {
+    name: "android.hardware.nfc",
+    vendor_available: true,
+    srcs: ["android/hardware/nfc/*.aidl"],
+    stability: "vintf",
+    backend: {
+        cpp: {
+            enabled: false,
+        },
+        java: {
+            sdk_version: "module_current",
+            enabled: false,
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+        },
+    },
+}
diff --git a/nfc/aidl/TEST_MAPPING b/nfc/aidl/TEST_MAPPING
new file mode 100644
index 0000000..3a10084
--- /dev/null
+++ b/nfc/aidl/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "VtsAidlHalNfcTargetTest"
+    }
+  ]
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfc.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfc.aidl
new file mode 100644
index 0000000..7a0ae54
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfc.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.nfc;
+@VintfStability
+interface INfc {
+  void open(in android.hardware.nfc.INfcClientCallback clientCallback);
+  void close(in android.hardware.nfc.NfcCloseType type);
+  void coreInitialized();
+  void factoryReset();
+  android.hardware.nfc.NfcConfig getConfig();
+  void powerCycle();
+  void preDiscover();
+  int write(in byte[] data);
+  void setEnableVerboseLogging(in boolean enable);
+  boolean isVerboseLoggingEnabled();
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfcClientCallback.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfcClientCallback.aidl
new file mode 100644
index 0000000..8150e81
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfcClientCallback.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.nfc;
+@VintfStability
+interface INfcClientCallback {
+  void sendData(in byte[] data);
+  void sendEvent(in android.hardware.nfc.NfcEvent event, in android.hardware.nfc.NfcStatus status);
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcCloseType.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcCloseType.aidl
new file mode 100644
index 0000000..7d44d48
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcCloseType.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.nfc;
+@Backing(type="int") @VintfStability
+enum NfcCloseType {
+  DISABLE = 0,
+  HOST_SWITCHED_OFF = 1,
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcConfig.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcConfig.aidl
new file mode 100644
index 0000000..92e0a9a
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcConfig.aidl
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.nfc;
+@VintfStability
+parcelable NfcConfig {
+  boolean nfaPollBailOutMode;
+  android.hardware.nfc.PresenceCheckAlgorithm presenceCheckAlgorithm;
+  android.hardware.nfc.ProtocolDiscoveryConfig nfaProprietaryCfg;
+  byte defaultOffHostRoute;
+  byte defaultOffHostRouteFelica;
+  byte defaultSystemCodeRoute;
+  byte defaultSystemCodePowerState;
+  byte defaultRoute;
+  byte offHostESEPipeId;
+  byte offHostSIMPipeId;
+  int maxIsoDepTransceiveLength;
+  byte[] hostAllowlist;
+  byte[] offHostRouteUicc;
+  byte[] offHostRouteEse;
+  byte defaultIsoDepRoute;
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcEvent.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcEvent.aidl
new file mode 100644
index 0000000..dda258e
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcEvent.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.nfc;
+@Backing(type="int") @VintfStability
+enum NfcEvent {
+  OPEN_CPLT = 0,
+  CLOSE_CPLT = 1,
+  POST_INIT_CPLT = 2,
+  PRE_DISCOVER_CPLT = 3,
+  HCI_NETWORK_RESET = 4,
+  ERROR = 5,
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcStatus.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcStatus.aidl
new file mode 100644
index 0000000..2632480
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcStatus.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.nfc;
+@Backing(type="int") @VintfStability
+enum NfcStatus {
+  OK = 0,
+  FAILED = 1,
+  ERR_TRANSPORT = 2,
+  ERR_CMD_TIMEOUT = 3,
+  REFUSED = 4,
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/PresenceCheckAlgorithm.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/PresenceCheckAlgorithm.aidl
new file mode 100644
index 0000000..9a9be21
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/PresenceCheckAlgorithm.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.nfc;
+@Backing(type="byte") @VintfStability
+enum PresenceCheckAlgorithm {
+  DEFAULT = 0,
+  I_BLOCK = 1,
+  ISO_DEP_NAK = 2,
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/ProtocolDiscoveryConfig.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/ProtocolDiscoveryConfig.aidl
new file mode 100644
index 0000000..021dfe2
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/ProtocolDiscoveryConfig.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.nfc;
+@VintfStability
+parcelable ProtocolDiscoveryConfig {
+  byte protocol18092Active;
+  byte protocolBPrime;
+  byte protocolDual;
+  byte protocol15693;
+  byte protocolKovio;
+  byte protocolMifare;
+  byte discoveryPollKovio;
+  byte discoveryPollBPrime;
+  byte discoveryListenBPrime;
+}
diff --git a/nfc/aidl/android/hardware/nfc/INfc.aidl b/nfc/aidl/android/hardware/nfc/INfc.aidl
new file mode 100644
index 0000000..662f8d4
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/INfc.aidl
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.nfc;
+
+import android.hardware.nfc.INfcClientCallback;
+import android.hardware.nfc.NfcCloseType;
+import android.hardware.nfc.NfcConfig;
+import android.hardware.nfc.NfcStatus;
+
+@VintfStability
+interface INfc {
+    /**
+     * Opens the NFC controller device and performs initialization.
+     * This may include patch download and other vendor-specific initialization.
+     *
+     * If open completes successfully, the controller must be ready to perform NCI
+     * initialization - ie accept CORE_RESET and subsequent commands through the write()
+     * call.
+     *
+     * Returns ok() if initialization starts successfully.
+     * If open() returns ok(), the NCI stack must wait for a NfcEvent.OPEN_CPLT
+     * callback before continuing.
+     * If a NfcEvent.OPEN_CPLT callback received with status NfcStatus::OK, the controller
+     * must be ready to perform  NCI initialization - ie accept CORE_RESET and subsequent
+     * commands through the write() call.
+     */
+    void open(in INfcClientCallback clientCallback);
+
+    /**
+     * Close the NFC HAL and setup NFC controller close type.
+     * Associated resources such as clientCallback must be released.
+     * The clientCallback reference from open() must be invalid after close().
+     * If close() returns ok(), the NCI stack must wait for a NfcEvent.CLOSE_CPLT
+     * callback before continuing.
+     * Returns an error if close may be called more than once.
+     * Calls to any other method which expect a callback after this must return
+     * a service-specific error NfcStatus::FAILED.
+     * HAL close automatically if the client drops the reference to the HAL or
+     * crashes.
+     */
+    void close(in NfcCloseType type);
+
+    /**
+     * coreInitialized() is called after the CORE_INIT_RSP is received from the
+     * NFCC. At this time, the HAL can do any chip-specific configuration.
+     *
+     * If coreInitialized() returns ok(), the NCI stack must wait for a
+     * NfcEvent.POST_INIT_CPLT before continuing.
+     * If coreInitialized() returns an error, the NCI stack must continue immediately.
+     *
+     * coreInitialized() must be called after open() registers the clientCallback
+     * or return a service-specific error NfcStatus::FAILED directly.
+     *
+     */
+    void coreInitialized();
+
+    /**
+     * Clears the NFC chip.
+     *
+     * Must be called during factory reset and/or before the first time the HAL is
+     * initialized after a factory reset.
+     */
+    void factoryReset();
+
+    /**
+     * Fetches vendor specific configurations.
+     * @return NfcConfig indicates support for certain features and
+     * populates the vendor specific configs.
+     */
+    NfcConfig getConfig();
+
+    /**
+     * Restart controller by power cyle;
+     * It's similar to open but just reset the controller without initialize all the
+     * resources.
+     *
+     * If powerCycle() returns ok(), the NCI stack must wait for a NfcEvent.OPEN_CPLT
+     * before continuing.
+     *
+     * powerCycle() must be called after open() registers the clientCallback
+     * or return a service-specific error NfcStatus::FAILED directly.
+     */
+    void powerCycle();
+
+    /**
+     * preDiscover is called every time before starting RF discovery.
+     * It is a good place to do vendor-specific configuration that must be
+     * performed every time RF discovery is about to be started.
+     *
+     * If preDiscover() returns ok(), the NCI stack must wait for a
+     * NfcEvent.PREDISCOVER_CPLT before continuing.
+     *
+     * preDiscover() must be called after open() registers the clientCallback
+     * or return a service-specific error NfcStatus::FAILED directly.
+     *
+     * If preDiscover() reports an error, the NCI stack must start RF discovery immediately.
+     */
+    void preDiscover();
+
+    /**
+     * Performs an NCI write.
+     *
+     * This method may queue writes and return immediately. The only
+     * requirement is that the writes are executed in order.
+     *
+     * @param data
+     * Data packet to transmit NCI Commands and Data Messages over write.
+     * Detailed format is defined in NFC Controller Interface (NCI) Technical Specification.
+     * https://nfc-forum.org/our-work/specification-releases/
+     *
+     * @return number of bytes written to the NFCC.
+     */
+    int write(in byte[] data);
+
+    /**
+     * Set the logging flag for NFC HAL to enable it's verbose logging.
+     * If verbose logging is not supported, the call must not have any effect on logging verbosity.
+     * However, isVerboseLoggingEnabled() must still return the value set by the last call to
+     * setEnableVerboseLogging().
+     * @param enable for setting the verbose logging flag to HAL
+     */
+    void setEnableVerboseLogging(in boolean enable);
+
+    /**
+     * Get the verbose logging flag value from NFC HAL.
+     * @return true if verbose logging flag value is enabled, false if disabled.
+     */
+    boolean isVerboseLoggingEnabled();
+}
diff --git a/nfc/aidl/android/hardware/nfc/INfcClientCallback.aidl b/nfc/aidl/android/hardware/nfc/INfcClientCallback.aidl
new file mode 100644
index 0000000..43d4f5c
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/INfcClientCallback.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.nfc;
+
+import android.hardware.nfc.NfcEvent;
+import android.hardware.nfc.NfcStatus;
+
+@VintfStability
+interface INfcClientCallback {
+    /**
+     * The callback passed in from the NFC stack that the HAL
+     * can use to pass incomming response data to the stack.
+     *
+     * @param data
+     * Data packet to transmit NCI Responses, Notifications, and Data
+     * Messages over sendData.
+     * Detailed format is defined in NFC Controller Interface (NCI) Technical Specification.
+     * https://nfc-forum.org/our-work/specification-releases/
+     */
+    void sendData(in byte[] data);
+
+    /**
+     * The callback passed in from the NFC stack that the HAL
+     * can use to pass events back to the stack.
+     */
+    void sendEvent(in NfcEvent event, in NfcStatus status);
+}
diff --git a/nfc/aidl/android/hardware/nfc/NfcCloseType.aidl b/nfc/aidl/android/hardware/nfc/NfcCloseType.aidl
new file mode 100644
index 0000000..5160532
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/NfcCloseType.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.nfc;
+
+/**
+ * Different closing types for NFC HAL.
+ */
+@VintfStability
+@Backing(type="int")
+enum NfcCloseType {
+    /**
+     * Close the NFC controller and free NFC HAL resources.
+     */
+    DISABLE = 0,
+
+    /**
+     * Switch the NFC controller operation mode and free NFC HAL resources.
+     * Enable NFC functionality for off host card emulation usecases in
+     * device(host) power off(switched off) state, if the device
+     * supports power off use cases. If the device doesn't support power
+     * off use cases, this call should be same as DISABLE.
+     */
+    HOST_SWITCHED_OFF = 1,
+}
diff --git a/nfc/aidl/android/hardware/nfc/NfcConfig.aidl b/nfc/aidl/android/hardware/nfc/NfcConfig.aidl
new file mode 100644
index 0000000..1b4fcfb
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/NfcConfig.aidl
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.nfc;
+
+import android.hardware.nfc.PresenceCheckAlgorithm;
+import android.hardware.nfc.ProtocolDiscoveryConfig;
+
+/**
+ * Define Nfc related configurations based on:
+ * NFC Controller Interface (NCI) Technical Specification
+ * https://nfc-forum.org/our-work/specification-releases/
+ */
+@VintfStability
+parcelable NfcConfig {
+    /**
+     * If true, NFCC is using bail out mode for either Type A or Type B poll
+     * based on: Nfc-Forum Activity Technical Specification
+     * https://nfc-forum.org/our-work/specification-releases/
+     */
+    boolean nfaPollBailOutMode;
+    PresenceCheckAlgorithm presenceCheckAlgorithm;
+    ProtocolDiscoveryConfig nfaProprietaryCfg;
+    /**
+     * Default off-host route. 0x00 if there aren't any. Refer to NCI spec.
+     */
+    byte defaultOffHostRoute;
+    /**
+     * Default off-host route for Felica. 0x00 if there aren't any. Refer to
+     * NCI spec.
+     */
+    byte defaultOffHostRouteFelica;
+    /**
+     * Default system code route. 0x00 if there aren't any. Refer NCI spec.
+     */
+    byte defaultSystemCodeRoute;
+    /**
+     * Default power state for system code route. 0x00 if there aren't any.
+     * Refer to NCI spec.
+     */
+    byte defaultSystemCodePowerState;
+    /**
+     * Default route for all remaining protocols and technology which haven't
+     * been configured.
+     * Device Host(0x00) is the default. Refer to NCI spec.
+     *
+     */
+    byte defaultRoute;
+    /**
+     * Pipe ID for eSE. 0x00 if there aren't any.
+     */
+    byte offHostESEPipeId;
+    /**
+     * Pipe ID for UICC. 0x00 if there aren't any.
+     */
+    byte offHostSIMPipeId;
+    /**
+     * Extended APDU length for ISO_DEP. If not supported default length is 261
+     */
+    int maxIsoDepTransceiveLength;
+    /**
+     * list of allowed host ids, as per ETSI TS 102 622
+     * https://www.etsi.org/
+     */
+    byte[] hostAllowlist;
+    /**
+     * NFCEE ID for offhost UICC & eSE secure element.
+     * 0x00 if there aren't any. Refer to NCI spec.
+     */
+    byte[] offHostRouteUicc;
+    byte[] offHostRouteEse;
+    /**
+     * Default IsoDep route. 0x00 if there aren't any. Refer to NCI spec.
+     */
+    byte defaultIsoDepRoute;
+}
diff --git a/nfc/aidl/android/hardware/nfc/NfcEvent.aidl b/nfc/aidl/android/hardware/nfc/NfcEvent.aidl
new file mode 100644
index 0000000..a78b1cd
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/NfcEvent.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.nfc;
+
+/**
+ * Nfc event to notify nfc status change.
+ */
+@VintfStability
+@Backing(type="int")
+enum NfcEvent {
+    /**
+     * Open complete event to notify upper layer when NFC HAL and NFCC
+     * initialization complete.
+     */
+    OPEN_CPLT = 0,
+    /**
+     * Close complete event to notify upper layer when HAL close is done.
+     */
+    CLOSE_CPLT = 1,
+    /**
+     * Post init complete event to notify upper layer when post init operations
+     * are done.
+     */
+    POST_INIT_CPLT = 2,
+    /**
+     * Pre-discover complete event to notify upper layer when pre-discover
+     * operations are done.
+     */
+    PRE_DISCOVER_CPLT = 3,
+    /**
+     * HCI network reset event to notify upplayer when HCI network needs to
+     * be re-initialized in case of an error.
+     */
+    HCI_NETWORK_RESET = 4,
+    /**
+     * Error event to notify upper layer when there's an unknown error.
+     */
+    ERROR = 5,
+}
diff --git a/nfc/aidl/android/hardware/nfc/NfcStatus.aidl b/nfc/aidl/android/hardware/nfc/NfcStatus.aidl
new file mode 100644
index 0000000..a38d370
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/NfcStatus.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.nfc;
+
+/**
+ * Used to specify the status of the NfcEvent
+ */
+@VintfStability
+@Backing(type="int")
+enum NfcStatus {
+    /**
+     * Default status when NfcEvent with status OK.
+     */
+    OK = 0,
+    /**
+     * Generic error.
+     */
+    FAILED = 1,
+    /**
+     * Transport error.
+     */
+    ERR_TRANSPORT = 2,
+    /**
+     * Command timeout error.
+     */
+    ERR_CMD_TIMEOUT = 3,
+    /**
+     * Refused error when command is rejected.
+     */
+    REFUSED = 4,
+}
diff --git a/nfc/aidl/android/hardware/nfc/PresenceCheckAlgorithm.aidl b/nfc/aidl/android/hardware/nfc/PresenceCheckAlgorithm.aidl
new file mode 100644
index 0000000..20e7bff
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/PresenceCheckAlgorithm.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.nfc;
+
+/*
+ * Presence Check Algorithm as defined in ISO/IEC 14443-4:
+ * https://www.iso.org/standard/73599.html
+ */
+@VintfStability
+@Backing(type="byte")
+enum PresenceCheckAlgorithm {
+    /**
+     * Let the stack select an algorithm
+     */
+    DEFAULT = 0,
+    /**
+     * ISO-DEP protocol's empty I-block
+     */
+    I_BLOCK = 1,
+    /**
+     * Type - 4 tag protocol iso-dep nak presence check command is sent waiting for
+     * response and notification.
+     */
+    ISO_DEP_NAK = 2,
+}
diff --git a/nfc/aidl/android/hardware/nfc/ProtocolDiscoveryConfig.aidl b/nfc/aidl/android/hardware/nfc/ProtocolDiscoveryConfig.aidl
new file mode 100644
index 0000000..f8e3228
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/ProtocolDiscoveryConfig.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.nfc;
+
+/**
+ * Vendor Specific Proprietary Protocol & Discovery Configuration.
+ * Set to 0xFF if not supported.
+ * discovery* fields map to "RF Technology and Mode" in NCI Spec
+ * protocol* fields map to "RF protocols" in NCI Spec
+ */
+@VintfStability
+parcelable ProtocolDiscoveryConfig {
+    byte protocol18092Active;
+    byte protocolBPrime;
+    byte protocolDual;
+    byte protocol15693;
+    byte protocolKovio;
+    byte protocolMifare;
+    byte discoveryPollKovio;
+    byte discoveryPollBPrime;
+    byte discoveryListenBPrime;
+}
diff --git a/nfc/aidl/default/Android.bp b/nfc/aidl/default/Android.bp
new file mode 100644
index 0000000..907d23d
--- /dev/null
+++ b/nfc/aidl/default/Android.bp
@@ -0,0 +1,23 @@
+cc_binary {
+    name: "android.hardware.nfc-service.example",
+    relative_install_path: "hw",
+    init_rc: ["nfc-service-example.rc"],
+    vintf_fragments: ["nfc-service-example.xml"],
+    vendor: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libutils",
+        "libbinder_ndk",
+        "android.hardware.nfc-V1-ndk",
+    ],
+    srcs: [
+        "main.cpp",
+        "Nfc.cpp",
+        "Vendor_hal_api.cpp",
+    ],
+}
diff --git a/nfc/aidl/default/Nfc.cpp b/nfc/aidl/default/Nfc.cpp
new file mode 100644
index 0000000..4685b59
--- /dev/null
+++ b/nfc/aidl/default/Nfc.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Nfc.h"
+
+#include <android-base/logging.h>
+
+#include "Vendor_hal_api.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace nfc {
+
+std::shared_ptr<INfcClientCallback> Nfc::mCallback = nullptr;
+AIBinder_DeathRecipient* clientDeathRecipient = nullptr;
+
+void OnDeath(void* cookie) {
+    if (Nfc::mCallback != nullptr && !AIBinder_isAlive(Nfc::mCallback->asBinder().get())) {
+        LOG(INFO) << __func__ << " Nfc service has died";
+        Nfc* nfc = static_cast<Nfc*>(cookie);
+        nfc->close(NfcCloseType::DISABLE);
+    }
+}
+
+::ndk::ScopedAStatus Nfc::open(const std::shared_ptr<INfcClientCallback>& clientCallback) {
+    LOG(INFO) << "open";
+    if (clientCallback == nullptr) {
+        LOG(INFO) << "Nfc::open null callback";
+        return ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int32_t>(NfcStatus::FAILED));
+    }
+    Nfc::mCallback = clientCallback;
+
+    clientDeathRecipient = AIBinder_DeathRecipient_new(OnDeath);
+    auto linkRet = AIBinder_linkToDeath(clientCallback->asBinder().get(), clientDeathRecipient,
+                                        this /* cookie */);
+    if (linkRet != STATUS_OK) {
+        LOG(ERROR) << __func__ << ": linkToDeath failed: " << linkRet;
+        // Just ignore the error.
+    }
+
+    int ret = Vendor_hal_open(eventCallback, dataCallback);
+    return ret == 0 ? ndk::ScopedAStatus::ok()
+                    : ndk::ScopedAStatus::fromServiceSpecificError(
+                              static_cast<int32_t>(NfcStatus::FAILED));
+}
+
+::ndk::ScopedAStatus Nfc::close(NfcCloseType type) {
+    LOG(INFO) << "close";
+    if (Nfc::mCallback == nullptr) {
+        LOG(ERROR) << __func__ << "mCallback null";
+        return ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int32_t>(NfcStatus::FAILED));
+    }
+    int ret = 0;
+    if (type == NfcCloseType::HOST_SWITCHED_OFF) {
+        ret = Vendor_hal_close_off();
+    } else {
+        ret = Vendor_hal_close();
+    }
+    Nfc::mCallback = nullptr;
+    AIBinder_DeathRecipient_delete(clientDeathRecipient);
+    clientDeathRecipient = nullptr;
+    return ret == 0 ? ndk::ScopedAStatus::ok()
+                    : ndk::ScopedAStatus::fromServiceSpecificError(
+                              static_cast<int32_t>(NfcStatus::FAILED));
+}
+
+::ndk::ScopedAStatus Nfc::coreInitialized() {
+    LOG(INFO) << "coreInitialized";
+    if (Nfc::mCallback == nullptr) {
+        LOG(ERROR) << __func__ << "mCallback null";
+        return ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int32_t>(NfcStatus::FAILED));
+    }
+    int ret = Vendor_hal_core_initialized();
+
+    return ret == 0 ? ndk::ScopedAStatus::ok()
+                    : ndk::ScopedAStatus::fromServiceSpecificError(
+                              static_cast<int32_t>(NfcStatus::FAILED));
+}
+
+::ndk::ScopedAStatus Nfc::factoryReset() {
+    LOG(INFO) << "factoryReset";
+    Vendor_hal_factoryReset();
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus Nfc::getConfig(NfcConfig* _aidl_return) {
+    LOG(INFO) << "getConfig";
+    NfcConfig nfcVendorConfig;
+    Vendor_hal_getConfig(nfcVendorConfig);
+
+    *_aidl_return = nfcVendorConfig;
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus Nfc::powerCycle() {
+    LOG(INFO) << "powerCycle";
+    if (Nfc::mCallback == nullptr) {
+        LOG(ERROR) << __func__ << "mCallback null";
+        return ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int32_t>(NfcStatus::FAILED));
+    }
+    return Vendor_hal_power_cycle() ? ndk::ScopedAStatus::fromServiceSpecificError(
+                                              static_cast<int32_t>(NfcStatus::FAILED))
+                                    : ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus Nfc::preDiscover() {
+    LOG(INFO) << "preDiscover";
+    if (Nfc::mCallback == nullptr) {
+        LOG(ERROR) << __func__ << "mCallback null";
+        return ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int32_t>(NfcStatus::FAILED));
+    }
+    return Vendor_hal_pre_discover() ? ndk::ScopedAStatus::fromServiceSpecificError(
+                                               static_cast<int32_t>(NfcStatus::FAILED))
+                                     : ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus Nfc::write(const std::vector<uint8_t>& data, int32_t* _aidl_return) {
+    LOG(INFO) << "write";
+    if (Nfc::mCallback == nullptr) {
+        LOG(ERROR) << __func__ << "mCallback null";
+        return ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int32_t>(NfcStatus::FAILED));
+    }
+    *_aidl_return = Vendor_hal_write(data.size(), &data[0]);
+    return ndk::ScopedAStatus::ok();
+}
+::ndk::ScopedAStatus Nfc::setEnableVerboseLogging(bool enable) {
+    LOG(INFO) << "setVerboseLogging";
+    Vendor_hal_setVerboseLogging(enable);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus Nfc::isVerboseLoggingEnabled(bool* _aidl_return) {
+    *_aidl_return = Vendor_hal_getVerboseLogging();
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace nfc
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/nfc/aidl/default/Nfc.h b/nfc/aidl/default/Nfc.h
new file mode 100644
index 0000000..1b14534
--- /dev/null
+++ b/nfc/aidl/default/Nfc.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/nfc/BnNfc.h>
+#include <aidl/android/hardware/nfc/INfcClientCallback.h>
+#include <android-base/logging.h>
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace nfc {
+
+using ::aidl::android::hardware::nfc::NfcCloseType;
+using ::aidl::android::hardware::nfc::NfcConfig;
+using ::aidl::android::hardware::nfc::NfcStatus;
+
+// Default implementation that reports no support NFC.
+struct Nfc : public BnNfc {
+  public:
+    Nfc() = default;
+
+    ::ndk::ScopedAStatus open(const std::shared_ptr<INfcClientCallback>& clientCallback) override;
+    ::ndk::ScopedAStatus close(NfcCloseType type) override;
+    ::ndk::ScopedAStatus coreInitialized() override;
+    ::ndk::ScopedAStatus factoryReset() override;
+    ::ndk::ScopedAStatus getConfig(NfcConfig* _aidl_return) override;
+    ::ndk::ScopedAStatus powerCycle() override;
+    ::ndk::ScopedAStatus preDiscover() override;
+    ::ndk::ScopedAStatus write(const std::vector<uint8_t>& data, int32_t* _aidl_return) override;
+    ::ndk::ScopedAStatus setEnableVerboseLogging(bool enable) override;
+    ::ndk::ScopedAStatus isVerboseLoggingEnabled(bool* _aidl_return) override;
+
+    static void eventCallback(uint8_t event, uint8_t status) {
+        if (mCallback != nullptr) {
+            auto ret = mCallback->sendEvent((NfcEvent)event, (NfcStatus)status);
+            if (!ret.isOk()) {
+                LOG(ERROR) << "Failed to send event!";
+            }
+        }
+    }
+
+    static void dataCallback(uint16_t data_len, uint8_t* p_data) {
+        std::vector<uint8_t> data(p_data, p_data + data_len);
+        if (mCallback != nullptr) {
+            auto ret = mCallback->sendData(data);
+            if (!ret.isOk()) {
+                LOG(ERROR) << "Failed to send data!";
+            }
+        }
+    }
+
+    static std::shared_ptr<INfcClientCallback> mCallback;
+};
+
+}  // namespace nfc
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/nfc/aidl/default/Vendor_hal_api.cpp b/nfc/aidl/default/Vendor_hal_api.cpp
new file mode 100644
index 0000000..66a2ebc
--- /dev/null
+++ b/nfc/aidl/default/Vendor_hal_api.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/properties.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+
+#include "Vendor_hal_api.h"
+
+bool logging = false;
+
+int Vendor_hal_open(nfc_stack_callback_t* p_cback, nfc_stack_data_callback_t* p_data_cback) {
+    (void)p_cback;
+    (void)p_data_cback;
+    // nothing to open in this example
+    return -1;
+}
+
+int Vendor_hal_write(uint16_t data_len, const uint8_t* p_data) {
+    (void)data_len;
+    (void)p_data;
+    return -1;
+}
+
+int Vendor_hal_core_initialized() {
+    return -1;
+}
+
+int Vendor_hal_pre_discover() {
+    return -1;
+}
+
+int Vendor_hal_close() {
+    return -1;
+}
+
+int Vendor_hal_close_off() {
+    return -1;
+}
+
+int Vendor_hal_power_cycle() {
+    return -1;
+}
+
+void Vendor_hal_factoryReset() {}
+
+void Vendor_hal_getConfig(NfcConfig& config) {
+    (void)config;
+}
+
+void Vendor_hal_setVerboseLogging(bool enable) {
+    logging = enable;
+}
+
+bool Vendor_hal_getVerboseLogging() {
+    return logging;
+}
diff --git a/nfc/aidl/default/Vendor_hal_api.h b/nfc/aidl/default/Vendor_hal_api.h
new file mode 100644
index 0000000..595c2dd
--- /dev/null
+++ b/nfc/aidl/default/Vendor_hal_api.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <aidl/android/hardware/nfc/INfc.h>
+#include <aidl/android/hardware/nfc/NfcConfig.h>
+#include <aidl/android/hardware/nfc/NfcEvent.h>
+#include <aidl/android/hardware/nfc/NfcStatus.h>
+#include <aidl/android/hardware/nfc/PresenceCheckAlgorithm.h>
+#include <aidl/android/hardware/nfc/ProtocolDiscoveryConfig.h>
+#include "hardware_nfc.h"
+
+using aidl::android::hardware::nfc::NfcConfig;
+using aidl::android::hardware::nfc::NfcEvent;
+using aidl::android::hardware::nfc::NfcStatus;
+using aidl::android::hardware::nfc::PresenceCheckAlgorithm;
+using aidl::android::hardware::nfc::ProtocolDiscoveryConfig;
+
+int Vendor_hal_open(nfc_stack_callback_t* p_cback, nfc_stack_data_callback_t* p_data_cback);
+int Vendor_hal_write(uint16_t data_len, const uint8_t* p_data);
+
+int Vendor_hal_core_initialized();
+
+int Vendor_hal_pre_discover();
+
+int Vendor_hal_close();
+
+int Vendor_hal_close_off();
+
+int Vendor_hal_power_cycle();
+
+void Vendor_hal_factoryReset();
+
+void Vendor_hal_getConfig(NfcConfig& config);
+
+void Vendor_hal_setVerboseLogging(bool enable);
+
+bool Vendor_hal_getVerboseLogging();
diff --git a/nfc/aidl/default/hardware_nfc.h b/nfc/aidl/default/hardware_nfc.h
new file mode 100644
index 0000000..0f856c5
--- /dev/null
+++ b/nfc/aidl/default/hardware_nfc.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+typedef uint8_t nfc_event_t;
+typedef uint8_t nfc_status_t;
+
+/*
+ * The callback passed in from the NFC stack that the HAL
+ * can use to pass events back to the stack.
+ */
+typedef void(nfc_stack_callback_t)(nfc_event_t event, nfc_status_t event_status);
+
+/*
+ * The callback passed in from the NFC stack that the HAL
+ * can use to pass incomming data to the stack.
+ */
+typedef void(nfc_stack_data_callback_t)(uint16_t data_len, uint8_t* p_data);
diff --git a/nfc/aidl/default/main.cpp b/nfc/aidl/default/main.cpp
new file mode 100644
index 0000000..0cc51e7
--- /dev/null
+++ b/nfc/aidl/default/main.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "Nfc.h"
+using ::aidl::android::hardware::nfc::Nfc;
+
+int main() {
+    LOG(INFO) << "NFC HAL starting up";
+    if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) {
+        LOG(INFO) << "failed to set thread pool max thread count";
+        return 1;
+    }
+    std::shared_ptr<Nfc> nfc_service = ndk::SharedRefBase::make<Nfc>();
+
+    const std::string instance = std::string() + Nfc::descriptor + "/default";
+    binder_status_t status =
+            AServiceManager_addService(nfc_service->asBinder().get(), instance.c_str());
+    CHECK(status == STATUS_OK);
+    ABinderProcess_joinThreadPool();
+    return 0;
+}
diff --git a/nfc/aidl/default/nfc-service-example.rc b/nfc/aidl/default/nfc-service-example.rc
new file mode 100644
index 0000000..7d7052e
--- /dev/null
+++ b/nfc/aidl/default/nfc-service-example.rc
@@ -0,0 +1,4 @@
+service nfc_hal_service /vendor/bin/hw/android.hardware.nfc-service.st
+    class hal
+    user nfc
+    group nfc
diff --git a/nfc/aidl/default/nfc-service-example.xml b/nfc/aidl/default/nfc-service-example.xml
new file mode 100644
index 0000000..70fed20
--- /dev/null
+++ b/nfc/aidl/default/nfc-service-example.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.nfc</name>
+        <fqname>INfc/default</fqname>
+    </hal>
+</manifest>
diff --git a/nfc/aidl/vts/functional/Android.bp b/nfc/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..99eecd0
--- /dev/null
+++ b/nfc/aidl/vts/functional/Android.bp
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsAidlHalNfcTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: [
+        "VtsAidlHalNfcTargetTest.cpp",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libbinder_ndk",
+    ],
+    static_libs: [
+        "android.hardware.nfc-V1-ndk",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/nfc/aidl/vts/functional/VtsAidlHalNfcTargetTest.cpp b/nfc/aidl/vts/functional/VtsAidlHalNfcTargetTest.cpp
new file mode 100644
index 0000000..977b25c
--- /dev/null
+++ b/nfc/aidl/vts/functional/VtsAidlHalNfcTargetTest.cpp
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "nfc_aidl_hal_test"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/nfc/BnNfc.h>
+#include <aidl/android/hardware/nfc/BnNfcClientCallback.h>
+#include <aidl/android/hardware/nfc/INfc.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_enums.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <chrono>
+#include <future>
+
+using aidl::android::hardware::nfc::INfc;
+using aidl::android::hardware::nfc::INfcClientCallback;
+using aidl::android::hardware::nfc::NfcCloseType;
+using aidl::android::hardware::nfc::NfcConfig;
+using aidl::android::hardware::nfc::NfcEvent;
+using aidl::android::hardware::nfc::NfcStatus;
+using aidl::android::hardware::nfc::PresenceCheckAlgorithm;
+
+using android::getAidlHalInstanceNames;
+using android::PrintInstanceNameToString;
+using android::base::StringPrintf;
+using ndk::enum_range;
+using ndk::ScopedAStatus;
+using ndk::SharedRefBase;
+using ndk::SpAIBinder;
+
+constexpr static int kCallbackTimeoutMs = 10000;
+
+// 261 bytes is the default and minimum transceive length
+constexpr unsigned int MIN_ISO_DEP_TRANSCEIVE_LENGTH = 261;
+
+// Range of valid off host route ids
+constexpr uint8_t MIN_OFFHOST_ROUTE_ID = 0x01;
+constexpr uint8_t MAX_OFFHOST_ROUTE_ID = 0xFE;
+
+class NfcClientCallback : public aidl::android::hardware::nfc::BnNfcClientCallback {
+  public:
+    NfcClientCallback(const std::function<void(NfcEvent, NfcStatus)>& on_hal_event_cb,
+                      const std::function<void(const std::vector<uint8_t>&)>& on_nci_data_cb)
+        : on_nci_data_cb_(on_nci_data_cb), on_hal_event_cb_(on_hal_event_cb) {}
+    virtual ~NfcClientCallback() = default;
+
+    ::ndk::ScopedAStatus sendEvent(NfcEvent event, NfcStatus event_status) override {
+        on_hal_event_cb_(event, event_status);
+        return ::ndk::ScopedAStatus::ok();
+    };
+    ::ndk::ScopedAStatus sendData(const std::vector<uint8_t>& data) override {
+        on_nci_data_cb_(data);
+        return ::ndk::ScopedAStatus::ok();
+    };
+
+  private:
+    std::function<void(const std::vector<uint8_t>&)> on_nci_data_cb_;
+    std::function<void(NfcEvent, NfcStatus)> on_hal_event_cb_;
+};
+
+class NfcAidl : public testing::TestWithParam<std::string> {
+  public:
+    void SetUp() override {
+        SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+        infc_ = INfc::fromBinder(binder);
+        ASSERT_NE(infc_, nullptr);
+    }
+    std::shared_ptr<INfc> infc_;
+};
+
+/*
+ * OpenAndCloseForDisable:
+ * Makes an open call, waits for NfcEvent::OPEN_CPLT
+ * Immediately calls close(NfcCloseType::DISABLE) and
+ * waits for NfcEvent::CLOSE_CPLT
+ *
+ */
+TEST_P(NfcAidl, OpenAndCloseForDisable) {
+    std::promise<void> open_cb_promise;
+    std::promise<void> close_cb_promise;
+    auto open_cb_future = open_cb_promise.get_future();
+    auto close_cb_future = close_cb_promise.get_future();
+    std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+            [&open_cb_promise, &close_cb_promise](auto event, auto status) {
+                EXPECT_EQ(status, NfcStatus::OK);
+                LOG(INFO) << StringPrintf("%s,%d ", __func__, event);
+                if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value();
+                if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+            },
+            [](auto) {});
+    std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+    // Open and wait for OPEN_CPLT
+    LOG(INFO) << "open";
+    EXPECT_TRUE(infc_->open(mCallback).isOk());
+    EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+    // Close and wait for CLOSE_CPLT
+    LOG(INFO) << "close DISABLE";
+    EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk());
+    LOG(INFO) << "wait for close";
+    EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+}
+
+/*
+ * OpenAndCloseForHostSwitchedOff:
+ * Makes an open call, waits for NfcEvent::OPEN_CPLT
+ * Immediately calls close(NfcCloseType::HOST_SWITCHED_OFF) and
+ * waits for NfcEvent::CLOSE_CPLT
+ *
+ */
+TEST_P(NfcAidl, OpenAndCloseForHostSwitchedOff) {
+    std::promise<void> open_cb_promise;
+    std::promise<void> close_cb_promise;
+    auto open_cb_future = open_cb_promise.get_future();
+    auto close_cb_future = close_cb_promise.get_future();
+    std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+            [&open_cb_promise, &close_cb_promise](auto event, auto status) {
+                EXPECT_EQ(status, NfcStatus::OK);
+                if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value();
+                if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+            },
+            [](auto) {});
+    std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+    // Open and wait for OPEN_CPLT
+    LOG(INFO) << "open";
+    EXPECT_TRUE(infc_->open(mCallback).isOk());
+    EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+    // Close and wait for CLOSE_CPLT
+    LOG(INFO) << "close HOST_SWITCHED_OFF";
+    EXPECT_TRUE(infc_->close(NfcCloseType::HOST_SWITCHED_OFF).isOk());
+    EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+}
+
+/*
+ * OpenAfterOpen:
+ * Calls open() multiple times
+ * Checks status
+ */
+TEST_P(NfcAidl, OpenAfterOpen) {
+    int open_count = 0;
+    std::promise<void> open_cb_promise;
+    std::promise<void> open2_cb_promise;
+    auto open_cb_future = open_cb_promise.get_future();
+    auto open2_cb_future = open2_cb_promise.get_future();
+    std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+            [&open_cb_promise, &open2_cb_promise, &open_count](auto event, auto status) {
+                EXPECT_EQ(status, NfcStatus::OK);
+                if (event == NfcEvent::OPEN_CPLT) {
+                    open_count == 0 ? open_cb_promise.set_value() : open2_cb_promise.set_value();
+                    open_count++;
+                }
+            },
+            [](auto) {});
+
+    std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+    // Open and wait for OPEN_CPLT
+    LOG(INFO) << "open";
+    EXPECT_TRUE(infc_->open(mCallback).isOk());
+    EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+    // Open again and wait for OPEN_CPLT
+    LOG(INFO) << "open again";
+    EXPECT_TRUE(infc_->open(mCallback).isOk());
+    EXPECT_EQ(open2_cb_future.wait_for(timeout), std::future_status::ready);
+}
+
+/*
+ * CloseAfterClose:
+ * Calls close() multiple times
+ * Checks status
+ */
+TEST_P(NfcAidl, CloseAfterClose) {
+    std::promise<void> open_cb_promise;
+    std::promise<void> close_cb_promise;
+    auto open_cb_future = open_cb_promise.get_future();
+    auto close_cb_future = close_cb_promise.get_future();
+    std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+            [&open_cb_promise, &close_cb_promise](auto event, auto status) {
+                EXPECT_EQ(status, NfcStatus::OK);
+                if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value();
+                if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+            },
+            [](auto) {});
+    std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+    // Open and wait for OPEN_CPLT
+    LOG(INFO) << "open";
+    EXPECT_TRUE(infc_->open(mCallback).isOk());
+    EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+    // Close and wait for CLOSE_CPLT
+    LOG(INFO) << "close";
+    EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk());
+    EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+    // Close again should fail.
+    LOG(INFO) << "close again";
+    EXPECT_TRUE(!(infc_->close(NfcCloseType::DISABLE).isOk()));
+}
+
+/*
+ * PowerCycleAfterOpen:
+ * Calls powerCycle() after open
+ * Waits for NfcEvent.OPEN_CPLT
+ * Checks status
+ */
+TEST_P(NfcAidl, PowerCycleAfterOpen) {
+    int open_cplt_count = 0;
+    std::promise<void> open_cb_promise;
+    std::promise<void> power_cycle_cb_promise;
+    std::promise<void> close_cb_promise;
+    auto open_cb_future = open_cb_promise.get_future();
+    auto power_cycle_cb_future = power_cycle_cb_promise.get_future();
+    auto close_cb_future = close_cb_promise.get_future();
+    std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+            [&open_cb_promise, &close_cb_promise, &power_cycle_cb_promise, &open_cplt_count](
+                    auto event, auto status) {
+                EXPECT_EQ(status, NfcStatus::OK);
+                if (event == NfcEvent::OPEN_CPLT) {
+                    if (open_cplt_count == 0) {
+                        open_cplt_count++;
+                        open_cb_promise.set_value();
+                    } else {
+                        power_cycle_cb_promise.set_value();
+                    }
+                }
+                if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+            },
+            [](auto) {});
+    std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+    // Open and wait for OPEN_CPLT
+    LOG(INFO) << "open";
+    EXPECT_TRUE(infc_->open(mCallback).isOk());
+    EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+    // PowerCycle and wait for OPEN_CPLT
+    LOG(INFO) << "PowerCycle";
+    EXPECT_TRUE(infc_->powerCycle().isOk());
+    EXPECT_EQ(power_cycle_cb_future.wait_for(timeout), std::future_status::ready);
+
+    // Close and wait for CLOSE_CPLT
+    LOG(INFO) << "close";
+    EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk());
+    EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+}
+
+/*
+ * PowerCycleAfterClose:
+ * Calls powerCycle() after close
+ * PowerCycle should fail immediately
+ */
+TEST_P(NfcAidl, PowerCycleAfterClose) {
+    std::promise<void> open_cb_promise;
+    std::promise<void> close_cb_promise;
+    auto open_cb_future = open_cb_promise.get_future();
+    auto close_cb_future = close_cb_promise.get_future();
+    std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+            [&open_cb_promise, &close_cb_promise](auto event, auto status) {
+                EXPECT_EQ(status, NfcStatus::OK);
+                if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value();
+                if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+            },
+            [](auto) {});
+    std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+    // Open and wait for OPEN_CPLT
+    LOG(INFO) << "open";
+    EXPECT_TRUE(infc_->open(mCallback).isOk());
+    EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+    // Close and wait for CLOSE_CPLT
+    LOG(INFO) << "close";
+    EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk());
+    EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+
+    // PowerCycle should fail
+    LOG(INFO) << "PowerCycle";
+    EXPECT_TRUE(!(infc_->powerCycle().isOk()));
+}
+
+/*
+ * CoreInitializedAfterOpen:
+ * Calls coreInitialized() after open
+ * Waits for NfcEvent.POST_INIT_CPLT
+ */
+TEST_P(NfcAidl, CoreInitializedAfterOpen) {
+    std::promise<void> open_cb_promise;
+    std::promise<void> core_init_cb_promise;
+    std::promise<void> close_cb_promise;
+    auto open_cb_future = open_cb_promise.get_future();
+    auto core_init_cb_future = core_init_cb_promise.get_future();
+    auto close_cb_future = close_cb_promise.get_future();
+    std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+            [&open_cb_promise, &close_cb_promise, &core_init_cb_promise](auto event, auto status) {
+                EXPECT_EQ(status, NfcStatus::OK);
+                if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value();
+                if (event == NfcEvent::POST_INIT_CPLT) core_init_cb_promise.set_value();
+                if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+            },
+            [](auto) {});
+    std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+    // Open and wait for OPEN_CPLT
+    LOG(INFO) << "open";
+    EXPECT_TRUE(infc_->open(mCallback).isOk());
+    EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+    // CoreInitialized and wait for POST_INIT_CPLT
+    LOG(INFO) << "coreInitialized";
+    EXPECT_TRUE(infc_->coreInitialized().isOk());
+    EXPECT_EQ(core_init_cb_future.wait_for(timeout), std::future_status::ready);
+
+    // Close and wait for CLOSE_CPLT
+    LOG(INFO) << "close";
+    EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk());
+    EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+}
+
+/*
+ * CoreInitializedAfterClose:
+ * Calls coreInitialized() after close
+ * coreInitialized() should fail immediately
+ */
+TEST_P(NfcAidl, CoreInitializedAfterClose) {
+    std::promise<void> open_cb_promise;
+    std::promise<void> close_cb_promise;
+    auto open_cb_future = open_cb_promise.get_future();
+    auto close_cb_future = close_cb_promise.get_future();
+    std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+            [&open_cb_promise, &close_cb_promise](auto event, auto status) {
+                EXPECT_EQ(status, NfcStatus::OK);
+                if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value();
+                if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+            },
+            [](auto) {});
+    std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+    // Open and wait for OPEN_CPLT
+    LOG(INFO) << "open";
+    EXPECT_TRUE(infc_->open(mCallback).isOk());
+    EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+    // Close and wait for CLOSE_CPLT
+    LOG(INFO) << "close";
+    EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk());
+    EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+
+    // coreInitialized should fail
+    LOG(INFO) << "CoreInitialized";
+    EXPECT_TRUE(!(infc_->coreInitialized().isOk()));
+}
+
+/*
+ * PreDiscoverAfterClose:
+ * Call preDiscover() after close
+ * preDiscover() should fail immediately
+ */
+TEST_P(NfcAidl, PreDiscoverAfterClose) {
+    std::promise<void> open_cb_promise;
+    std::promise<void> close_cb_promise;
+    auto open_cb_future = open_cb_promise.get_future();
+    auto close_cb_future = close_cb_promise.get_future();
+    std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+            [&open_cb_promise, &close_cb_promise](auto event, auto status) {
+                EXPECT_EQ(status, NfcStatus::OK);
+                if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value();
+                if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+            },
+            [](auto) {});
+    std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+    // Open and wait for OPEN_CPLT
+    LOG(INFO) << "open";
+    EXPECT_TRUE(infc_->open(mCallback).isOk());
+    EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+    // Close and wait for CLOSE_CPLT
+    LOG(INFO) << "close";
+    EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk());
+    EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+
+    // preDiscover should fail
+    LOG(INFO) << "preDiscover";
+    EXPECT_TRUE(!(infc_->preDiscover().isOk()));
+}
+
+/*
+ * checkGetConfigValues:
+ * Calls getConfig()
+ * checks if fields in NfcConfig are populated correctly
+ */
+TEST_P(NfcAidl, CheckGetConfigValues) {
+    NfcConfig configValue;
+    EXPECT_TRUE(infc_->getConfig(&configValue).isOk());
+    EXPECT_GE(configValue.maxIsoDepTransceiveLength, MIN_ISO_DEP_TRANSCEIVE_LENGTH);
+    LOG(INFO) << StringPrintf("configValue.maxIsoDepTransceiveLength = %x",
+                              configValue.maxIsoDepTransceiveLength);
+    for (auto uicc : configValue.offHostRouteUicc) {
+        LOG(INFO) << StringPrintf("offHostRouteUicc = %x", uicc);
+        EXPECT_GE(uicc, MIN_OFFHOST_ROUTE_ID);
+        EXPECT_LE(uicc, MAX_OFFHOST_ROUTE_ID);
+    }
+    for (auto ese : configValue.offHostRouteEse) {
+        LOG(INFO) << StringPrintf("offHostRouteEse = %x", ese);
+        EXPECT_GE(ese, MIN_OFFHOST_ROUTE_ID);
+        EXPECT_LE(ese, MAX_OFFHOST_ROUTE_ID);
+    }
+    if (configValue.defaultIsoDepRoute != 0) {
+        EXPECT_GE((uint8_t)configValue.defaultIsoDepRoute, MIN_OFFHOST_ROUTE_ID);
+        EXPECT_LE((uint8_t)configValue.defaultIsoDepRoute, MAX_OFFHOST_ROUTE_ID);
+    }
+}
+
+/*
+ * CheckisVerboseLoggingEnabledAfterSetEnableVerboseLogging:
+ * Calls setEnableVerboseLogging()
+ * checks the return value of isVerboseLoggingEnabled
+ */
+TEST_P(NfcAidl, CheckisVerboseLoggingEnabledAfterSetEnableVerboseLogging) {
+    bool enabled = false;
+    EXPECT_TRUE(infc_->setEnableVerboseLogging(true).isOk());
+    EXPECT_TRUE(infc_->isVerboseLoggingEnabled(&enabled).isOk());
+    EXPECT_TRUE(enabled);
+    EXPECT_TRUE(infc_->setEnableVerboseLogging(false).isOk());
+    EXPECT_TRUE(infc_->isVerboseLoggingEnabled(&enabled).isOk());
+    EXPECT_TRUE(!enabled);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NfcAidl);
+INSTANTIATE_TEST_SUITE_P(Nfc, NfcAidl,
+                         testing::ValuesIn(::android::getAidlHalInstanceNames(INfc::descriptor)),
+                         ::android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ABinderProcess_startThreadPool();
+    std::system("/system/bin/svc nfc disable"); /* Turn off NFC */
+    sleep(5);
+    int status = RUN_ALL_TESTS();
+    LOG(INFO) << "Test result = " << status;
+    std::system("/system/bin/svc nfc enable"); /* Turn on NFC */
+    sleep(5);
+    return status;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/RpcHardwareInfo.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/RpcHardwareInfo.aidl
index 06bce19..5ff45f8 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/RpcHardwareInfo.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/RpcHardwareInfo.aidl
@@ -38,6 +38,7 @@
   int versionNumber;
   @utf8InCpp String rpcAuthorName;
   int supportedEekCurve = 0;
+  @nullable @utf8InCpp String uniqueId;
   const int CURVE_NONE = 0;
   const int CURVE_P256 = 1;
   const int CURVE_25519 = 2;
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index 9846ee9..4b63799 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -96,7 +96,9 @@
  *      - TRUSTED_ENVRIONMENT IKeyMintDevices must support curve 25519 for Purpose::SIGN (Ed25519,
  *        as specified in RFC 8032), Purpose::ATTEST_KEY (Ed25519) or for KeyPurpose::AGREE_KEY
  *        (X25519, as specified in RFC 7748).  However, a key must have exactly one of these
- *        purpose values; the same key cannot be used for multiple purposes.
+ *        purpose values; the same key cannot be used for multiple purposes. Signing operations
+ *        (Purpose::SIGN) have a message size limit of 16 KiB; operations on messages longer than
+ *        this limit must fail with ErrorCode::INVALID_INPUT_LENGTH.
  *        STRONGBOX IKeyMintDevices do not support curve 25519.
  *
  * o   AES
diff --git a/security/keymint/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl
index d297f87..3a4c233 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl
@@ -53,4 +53,21 @@
      * a passing implementation does not provide CURVE_NONE.
      */
     int supportedEekCurve = CURVE_NONE;
+
+    /**
+     * uniqueId is an opaque identifier for this IRemotelyProvisionedComponent implementation. The
+     * client should NOT interpret the content of the identifier in any way. The client can only
+     * compare identifiers to determine if two IRemotelyProvisionedComponents share the same
+     * implementation. Each IRemotelyProvisionedComponent implementation must have a distinct
+     * identifier from all other implementations on the same device.
+     *
+     * This identifier must be consistent across reboots, as it is used to store and track
+     * provisioned keys in a persistent, on-device database.
+     *
+     * uniqueId may not be empty, and must not be any longer than 32 characters.
+     *
+     * This field was added in API version 2.
+     *
+     */
+    @nullable @utf8InCpp String uniqueId;
 }
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 374f2da..146a527 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -25,6 +25,7 @@
 #include <cppbor_parse.h>
 #include <cutils/properties.h>
 #include <gmock/gmock.h>
+#include <openssl/evp.h>
 #include <openssl/mem.h>
 #include <remote_prov/remote_prov_utils.h>
 
@@ -206,6 +207,21 @@
     return boot_patch_level(key_characteristics_);
 }
 
+bool KeyMintAidlTestBase::Curve25519Supported() {
+    // Strongbox never supports curve 25519.
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        return false;
+    }
+
+    // Curve 25519 was included in version 2 of the KeyMint interface.
+    int32_t version = 0;
+    auto status = keymint_->getInterfaceVersion(&version);
+    if (!status.isOk()) {
+        ADD_FAILURE() << "Failed to determine interface version";
+    }
+    return version >= 2;
+}
+
 ErrorCode KeyMintAidlTestBase::GetReturnErrorCode(const Status& result) {
     if (result.isOk()) return ErrorCode::OK;
 
@@ -543,7 +559,12 @@
     std::vector<uint8_t> o_put;
     result = op_->update(vector<uint8_t>(input.begin(), input.end()), {}, {}, &o_put);
 
-    if (result.isOk()) output->append(o_put.begin(), o_put.end());
+    if (result.isOk()) {
+        output->append(o_put.begin(), o_put.end());
+    } else {
+        // Failure always terminates the operation.
+        op_ = {};
+    }
 
     return GetReturnErrorCode(result);
 }
@@ -740,6 +761,19 @@
 
     if (digest == Digest::NONE) {
         switch (EVP_PKEY_id(pub_key.get())) {
+            case EVP_PKEY_ED25519: {
+                ASSERT_EQ(64, signature.size());
+                uint8_t pub_keydata[32];
+                size_t pub_len = sizeof(pub_keydata);
+                ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(pub_key.get(), pub_keydata, &pub_len));
+                ASSERT_EQ(sizeof(pub_keydata), pub_len);
+                ASSERT_EQ(1, ED25519_verify(reinterpret_cast<const uint8_t*>(message.data()),
+                                            message.size(),
+                                            reinterpret_cast<const uint8_t*>(signature.data()),
+                                            pub_keydata));
+                break;
+            }
+
             case EVP_PKEY_EC: {
                 vector<uint8_t> data((EVP_PKEY_bits(pub_key.get()) + 7) / 8);
                 size_t data_size = std::min(data.size(), message.size());
@@ -1166,16 +1200,31 @@
 vector<EcCurve> KeyMintAidlTestBase::ValidCurves() {
     if (securityLevel_ == SecurityLevel::STRONGBOX) {
         return {EcCurve::P_256};
+    } else if (Curve25519Supported()) {
+        return {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521,
+                EcCurve::CURVE_25519};
     } else {
-        return {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521};
+        return {
+                EcCurve::P_224,
+                EcCurve::P_256,
+                EcCurve::P_384,
+                EcCurve::P_521,
+        };
     }
 }
 
 vector<EcCurve> KeyMintAidlTestBase::InvalidCurves() {
     if (SecLevel() == SecurityLevel::STRONGBOX) {
-        return {EcCurve::P_224, EcCurve::P_384, EcCurve::P_521};
+        // Curve 25519 is not supported, either because:
+        // - KeyMint v1: it's an unknown enum value
+        // - KeyMint v2+: it's not supported by StrongBox.
+        return {EcCurve::P_224, EcCurve::P_384, EcCurve::P_521, EcCurve::CURVE_25519};
     } else {
-        return {};
+        if (Curve25519Supported()) {
+            return {};
+        } else {
+            return {EcCurve::CURVE_25519};
+        }
     }
 }
 
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index 61f9d4d..27cb99c 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -80,6 +80,8 @@
     uint32_t boot_patch_level(const vector<KeyCharacteristics>& key_characteristics);
     uint32_t boot_patch_level();
 
+    bool Curve25519Supported();
+
     ErrorCode GetReturnErrorCode(const Status& result);
 
     ErrorCode GenerateKey(const AuthorizationSet& key_desc, vector<uint8_t>* key_blob,
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index d109058..62f22bb 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -22,6 +22,7 @@
 #include <algorithm>
 #include <iostream>
 
+#include <openssl/curve25519.h>
 #include <openssl/ec.h>
 #include <openssl/evp.h>
 #include <openssl/mem.h>
@@ -69,6 +70,9 @@
 
 namespace {
 
+// Maximum supported Ed25519 message size.
+const size_t MAX_ED25519_MSG_SIZE = 16 * 1024;
+
 // Whether to check that BOOT_PATCHLEVEL is populated.
 bool check_boot_pl = true;
 
@@ -415,6 +419,126 @@
         // } end SEQUENCE (PrivateKeyInfo)
 );
 
+/**
+ * Ed25519 key pair generated as follows:
+ * ```
+ * % openssl req -x509 -newkey ED25519 -days 700 -nodes \
+ *  -keyout ed25519_priv.key -out ed25519.pem * -subj "/CN=fake.ed25519.com"
+ * Generating a ED25519 private key writing new private key to
+ * 'ed25519_priv.key'
+ * -----
+ * % cat ed25519_priv.key
+ * -----BEGIN PRIVATE KEY-----
+ * MC4CAQAwBQYDK2VwBCIEIKl3A5quNywcj1P+0XI9SBalFPIvO52NxceMLRH6dVmR
+ * -----END PRIVATE KEY-----
+ * % der2ascii -pem -i ed25519_priv.key
+ * SEQUENCE {
+ *   INTEGER { 0 }
+ *   SEQUENCE {
+ *     # ed25519
+ *     OBJECT_IDENTIFIER { 1.3.101.112 }
+ *   }
+ *   OCTET_STRING {
+ *     OCTET_STRING { `a977039aae372c1c8f53fed1723d4816a514f22f3b9d8dc5c78c2d11fa755991` }
+ *   }
+ * }
+ * % cat ed25519.pem
+ * -----BEGIN CERTIFICATE-----
+ * MIIBSjCB/aADAgECAhR0Jron3eKcdgqyecv/eEfGWAzn8DAFBgMrZXAwGzEZMBcG
+ * A1UEAwwQZmFrZS5lZDI1NTE5LmNvbTAeFw0yMTEwMjAwODI3NDJaFw0yMzA5MjAw
+ * ODI3NDJaMBsxGTAXBgNVBAMMEGZha2UuZWQyNTUxOS5jb20wKjAFBgMrZXADIQDv
+ * uwHz+3TaQ69D2digxlz0fFfsZg0rPqgQae3jBPRWkaNTMFEwHQYDVR0OBBYEFN9O
+ * od30SY4JTs66ZR403UPya+iXMB8GA1UdIwQYMBaAFN9Ood30SY4JTs66ZR403UPy
+ * a+iXMA8GA1UdEwEB/wQFMAMBAf8wBQYDK2VwA0EAKjVrYQjuE/gEL2j/ABpDbFjV
+ * Ilg5tJ6MN/P3psAv3Cs7f0X1lFqdlt15nJ/6aj2cmGCwNRXt5wcyYDKNu+v2Dw==
+ * -----END CERTIFICATE-----
+ * % openssl x509 -in ed25519.pem -text -noout
+ * Certificate:
+ *     Data:
+ *         Version: 3 (0x2)
+ *         Serial Number:
+ *             74:26:ba:27:dd:e2:9c:76:0a:b2:79:cb:ff:78:47:c6:58:0c:e7:f0
+ *         Signature Algorithm: ED25519
+ *         Issuer: CN = fake.ed25519.com
+ *         Validity
+ *             Not Before: Oct 20 08:27:42 2021 GMT
+ *             Not After : Sep 20 08:27:42 2023 GMT
+ *         Subject: CN = fake.ed25519.com
+ *         Subject Public Key Info:
+ *             Public Key Algorithm: ED25519
+ *                 ED25519 Public-Key:
+ *                 pub:
+ *                     ef:bb:01:f3:fb:74:da:43:af:43:d9:d8:a0:c6:5c:
+ *                     f4:7c:57:ec:66:0d:2b:3e:a8:10:69:ed:e3:04:f4:
+ *                     56:91
+ *         X509v3 extensions:
+ *             X509v3 Subject Key Identifier:
+ *                 DF:4E:A1:DD:F4:49:8E:09:4E:CE:BA:65:1E:34:DD:43:F2:6B:E8:97
+ *             X509v3 Authority Key Identifier:
+ *                 keyid:DF:4E:A1:DD:F4:49:8E:09:4E:CE:BA:65:1E:34:DD:43:F2:6B:E8:97
+ *
+ *             X509v3 Basic Constraints: critical
+ *                 CA:TRUE
+ *     Signature Algorithm: ED25519
+ *          2a:35:6b:61:08:ee:13:f8:04:2f:68:ff:00:1a:43:6c:58:d5:
+ *          22:58:39:b4:9e:8c:37:f3:f7:a6:c0:2f:dc:2b:3b:7f:45:f5:
+ *          94:5a:9d:96:dd:79:9c:9f:fa:6a:3d:9c:98:60:b0:35:15:ed:
+ *          e7:07:32:60:32:8d:bb:eb:f6:0f
+ * ```
+ */
+string ed25519_key = hex2str("a977039aae372c1c8f53fed1723d4816a514f22f3b9d8dc5c78c2d11fa755991");
+string ed25519_pkcs8_key = hex2str(
+        // RFC 5208 s5
+        "302e"    // SEQUENCE length 0x2e (PrivateKeyInfo) {
+        "0201"    // INTEGER length 1 (Version)
+        "00"      // version 0
+        "3005"    // SEQUENCE length 05 (AlgorithmIdentifier) {
+        "0603"    // OBJECT IDENTIFIER length 3 (algorithm)
+        "2b6570"  // 1.3.101.112 (id-Ed125519 RFC 8410 s3)
+        // } end SEQUENCE (AlgorithmIdentifier)
+        "0422"  // OCTET STRING length 0x22 (PrivateKey)
+        "0420"  // OCTET STRING length 0x20 (RFC 8410 s7)
+        "a977039aae372c1c8f53fed1723d4816a514f22f3b9d8dc5c78c2d11fa755991"
+        // } end SEQUENCE (PrivateKeyInfo)
+);
+string ed25519_pubkey = hex2str("efbb01f3fb74da43af43d9d8a0c65cf47c57ec660d2b3ea81069ede304f45691");
+
+/**
+ * X25519 key pair generated as follows:
+ * ```
+ * % openssl genpkey -algorithm X25519 > x25519_priv.key
+ * % cat x25519_priv.key
+ * -----BEGIN PRIVATE KEY-----
+ * MC4CAQAwBQYDK2VuBCIEIGgPwF3NLwQx/Sfwr2nfJvXitwlDNh3Skzh+TISN/y1C
+ * -----END PRIVATE KEY-----
+ * % der2ascii -pem -i x25519_priv.key
+ * SEQUENCE {
+ *   INTEGER { 0 }
+ *   SEQUENCE {
+ *     # x25519
+ *     OBJECT_IDENTIFIER { 1.3.101.110 }
+ *   }
+ *   OCTET_STRING {
+ *     OCTET_STRING { `680fc05dcd2f0431fd27f0af69df26f5e2b70943361dd293387e4c848dff2d42` }
+ *   }
+ * }
+ * ```
+ */
+
+string x25519_key = hex2str("680fc05dcd2f0431fd27f0af69df26f5e2b70943361dd293387e4c848dff2d42");
+string x25519_pkcs8_key = hex2str(
+        // RFC 5208 s5
+        "302e"    // SEQUENCE length 0x2e (PrivateKeyInfo) {
+        "0201"    // INTEGER length 1 (Version)
+        "00"      // version 0
+        "3005"    // SEQUENCE length 05 (AlgorithmIdentifier) {
+        "0603"    // OBJECT IDENTIFIER length 3 (algorithm)
+        "2b656e"  // 1.3.101.110 (id-X125519 RFC 8410 s3)
+        "0422"    // OCTET STRING length 0x22 (PrivateKey)
+        "0420"    // OCTET STRING length 0x20 (RFC 8410 s7)
+        "680fc05dcd2f0431fd27f0af69df26f5e2b70943361dd293387e4c848dff2d42");
+string x25519_pubkey = hex2str("be46925a857f17831d6d454b9d3d36a4a30166edf80eb82b684661c3e258f768");
+
 struct RSA_Delete {
     void operator()(RSA* p) { RSA_free(p); }
 };
@@ -1374,7 +1498,7 @@
 /*
  * NewKeyGenerationTest.Ecdsa
  *
- * Verifies that keymint can generate all required EC key sizes, and that the resulting keys
+ * Verifies that keymint can generate all required EC curves, and that the resulting keys
  * have correct characteristics.
  */
 TEST_P(NewKeyGenerationTest, Ecdsa) {
@@ -1400,6 +1524,65 @@
 }
 
 /*
+ * NewKeyGenerationTest.EcdsaCurve25519
+ *
+ * Verifies that keymint can generate a curve25519 key, and that the resulting key
+ * has correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaCurve25519) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    EcCurve curve = EcCurve::CURVE_25519;
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+    ErrorCode result = GenerateKey(AuthorizationSetBuilder()
+                                           .EcdsaSigningKey(curve)
+                                           .Digest(Digest::NONE)
+                                           .SetDefaultValidity(),
+                                   &key_blob, &key_characteristics);
+    ASSERT_EQ(result, ErrorCode::OK);
+    ASSERT_GT(key_blob.size(), 0U);
+
+    EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+    ASSERT_GT(cert_chain_.size(), 0);
+
+    CheckBaseParams(key_characteristics);
+    CheckCharacteristics(key_blob, key_characteristics);
+
+    AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+    EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+    EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
+
+    CheckedDeleteKey(&key_blob);
+}
+
+/*
+ * NewKeyGenerationTest.EcCurve25519MultiPurposeFail
+ *
+ * Verifies that KeyMint rejects an attempt to generate a curve 25519 key for both
+ * SIGN and AGREE_KEY.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaCurve25519MultiPurposeFail) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    EcCurve curve = EcCurve::CURVE_25519;
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+    ErrorCode result = GenerateKey(AuthorizationSetBuilder()
+                                           .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+                                           .EcdsaSigningKey(curve)
+                                           .Digest(Digest::NONE)
+                                           .SetDefaultValidity(),
+                                   &key_blob, &key_characteristics);
+    ASSERT_EQ(result, ErrorCode::INCOMPATIBLE_PURPOSE);
+}
+
+/*
  * NewKeyGenerationTest.EcdsaAttestation
  *
  * Verifies that for all Ecdsa key sizes, if challenge and app id is provided,
@@ -1453,6 +1636,62 @@
 }
 
 /*
+ * NewKeyGenerationTest.EcdsaAttestationCurve25519
+ *
+ * Verifies that for a curve 25519 key, if challenge and app id is provided,
+ * an attestation will be generated.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaAttestationCurve25519) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    EcCurve curve = EcCurve::CURVE_25519;
+    auto challenge = "hello";
+    auto app_id = "foo";
+
+    auto subject = "cert subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 0xFFFFFFFFFFFFFFFF;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+    ErrorCode result = GenerateKey(AuthorizationSetBuilder()
+                                           .Authorization(TAG_NO_AUTH_REQUIRED)
+                                           .EcdsaSigningKey(curve)
+                                           .Digest(Digest::NONE)
+                                           .AttestationChallenge(challenge)
+                                           .AttestationApplicationId(app_id)
+                                           .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                           .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                           .SetDefaultValidity(),
+                                   &key_blob, &key_characteristics);
+    ASSERT_EQ(ErrorCode::OK, result);
+    ASSERT_GT(key_blob.size(), 0U);
+    CheckBaseParams(key_characteristics);
+    CheckCharacteristics(key_blob, key_characteristics);
+
+    AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+    EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+    EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
+
+    EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+    ASSERT_GT(cert_chain_.size(), 0);
+    verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
+
+    AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+    AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+    EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id,  //
+                                          sw_enforced, hw_enforced, SecLevel(),
+                                          cert_chain_[0].encodedCertificate));
+
+    CheckedDeleteKey(&key_blob);
+}
+
+/*
  * NewKeyGenerationTest.EcdsaAttestationTags
  *
  * Verifies that creation of an attested ECDSA key includes various tags in the
@@ -1984,20 +2223,22 @@
 }
 
 /*
- * NewKeyGenerationTest.EcdsaInvalidSize
+ * NewKeyGenerationTest.EcdsaInvalidCurve
  *
- * Verifies that specifying an invalid key size for EC key generation returns
+ * Verifies that specifying an invalid curve for EC key generation returns
  * UNSUPPORTED_KEY_SIZE.
  */
-TEST_P(NewKeyGenerationTest, EcdsaInvalidSize) {
+TEST_P(NewKeyGenerationTest, EcdsaInvalidCurve) {
     for (auto curve : InvalidCurves()) {
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
-        ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE, GenerateKey(AuthorizationSetBuilder()
-                                                                       .EcdsaSigningKey(curve)
-                                                                       .Digest(Digest::NONE)
-                                                                       .SetDefaultValidity(),
-                                                               &key_blob, &key_characteristics));
+        auto result = GenerateKey(AuthorizationSetBuilder()
+                                          .EcdsaSigningKey(curve)
+                                          .Digest(Digest::NONE)
+                                          .SetDefaultValidity(),
+                                  &key_blob, &key_characteristics);
+        ASSERT_TRUE(result == ErrorCode::UNSUPPORTED_KEY_SIZE ||
+                    result == ErrorCode::UNSUPPORTED_EC_CURVE);
     }
 
     ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
@@ -2808,15 +3049,19 @@
 /*
  * SigningOperationsTest.EcdsaAllDigestsAndCurves
  *
- * Verifies ECDSA signature/verification for all digests and curves.
+ * Verifies ECDSA signature/verification for all digests and required curves.
  */
 TEST_P(SigningOperationsTest, EcdsaAllDigestsAndCurves) {
-    auto digests = ValidDigests(true /* withNone */, false /* withMD5 */);
 
     string message = "1234567890";
     string corrupt_message = "2234567890";
     for (auto curve : ValidCurves()) {
         SCOPED_TRACE(testing::Message() << "Curve::" << curve);
+        // Ed25519 only allows Digest::NONE.
+        auto digests = (curve == EcCurve::CURVE_25519)
+                               ? std::vector<Digest>(1, Digest::NONE)
+                               : ValidDigests(true /* withNone */, false /* withMD5 */);
+
         ErrorCode error = GenerateKey(AuthorizationSetBuilder()
                                               .Authorization(TAG_NO_AUTH_REQUIRED)
                                               .EcdsaSigningKey(curve)
@@ -2841,25 +3086,141 @@
 /*
  * SigningOperationsTest.EcdsaAllCurves
  *
- * Verifies that ECDSA operations succeed with all possible curves.
+ * Verifies that ECDSA operations succeed with all required curves.
  */
 TEST_P(SigningOperationsTest, EcdsaAllCurves) {
     for (auto curve : ValidCurves()) {
+        Digest digest = (curve == EcCurve::CURVE_25519 ? Digest::NONE : Digest::SHA_2_256);
+        SCOPED_TRACE(testing::Message() << "Curve::" << curve);
         ErrorCode error = GenerateKey(AuthorizationSetBuilder()
                                               .Authorization(TAG_NO_AUTH_REQUIRED)
                                               .EcdsaSigningKey(curve)
-                                              .Digest(Digest::SHA_2_256)
+                                              .Digest(digest)
                                               .SetDefaultValidity());
         EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
         if (error != ErrorCode::OK) continue;
 
         string message(1024, 'a');
-        SignMessage(message, AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
+        SignMessage(message, AuthorizationSetBuilder().Digest(digest));
         CheckedDeleteKey();
     }
 }
 
 /*
+ * SigningOperationsTest.EcdsaCurve25519
+ *
+ * Verifies that ECDSA operations succeed with curve25519.
+ */
+TEST_P(SigningOperationsTest, EcdsaCurve25519) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    EcCurve curve = EcCurve::CURVE_25519;
+    ErrorCode error = GenerateKey(AuthorizationSetBuilder()
+                                          .Authorization(TAG_NO_AUTH_REQUIRED)
+                                          .EcdsaSigningKey(curve)
+                                          .Digest(Digest::NONE)
+                                          .SetDefaultValidity());
+    ASSERT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
+
+    string message(1024, 'a');
+    SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE));
+    CheckedDeleteKey();
+}
+
+/*
+ * SigningOperationsTest.EcdsaCurve25519MaxSize
+ *
+ * Verifies that EDDSA operations with curve25519 under the maximum message size succeed.
+ */
+TEST_P(SigningOperationsTest, EcdsaCurve25519MaxSize) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    EcCurve curve = EcCurve::CURVE_25519;
+    ErrorCode error = GenerateKey(AuthorizationSetBuilder()
+                                          .Authorization(TAG_NO_AUTH_REQUIRED)
+                                          .EcdsaSigningKey(curve)
+                                          .Digest(Digest::NONE)
+                                          .SetDefaultValidity());
+    ASSERT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
+
+    auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
+
+    for (size_t msg_size : {MAX_ED25519_MSG_SIZE - 1, MAX_ED25519_MSG_SIZE}) {
+        SCOPED_TRACE(testing::Message() << "-msg-size=" << msg_size);
+        string message(msg_size, 'a');
+
+        // Attempt to sign via Begin+Finish.
+        AuthorizationSet out_params;
+        ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params));
+        EXPECT_TRUE(out_params.empty());
+        string signature;
+        auto result = Finish(message, &signature);
+        EXPECT_EQ(result, ErrorCode::OK);
+        LocalVerifyMessage(message, signature, params);
+
+        // Attempt to sign via Begin+Update+Finish
+        ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params));
+        EXPECT_TRUE(out_params.empty());
+        string output;
+        result = Update(message, &output);
+        EXPECT_EQ(result, ErrorCode::OK);
+        EXPECT_EQ(output.size(), 0);
+        string signature2;
+        EXPECT_EQ(ErrorCode::OK, Finish({}, &signature2));
+        LocalVerifyMessage(message, signature2, params);
+    }
+
+    CheckedDeleteKey();
+}
+
+/*
+ * SigningOperationsTest.EcdsaCurve25519MaxSizeFail
+ *
+ * Verifies that EDDSA operations with curve25519 fail when message size is too large.
+ */
+TEST_P(SigningOperationsTest, EcdsaCurve25519MaxSizeFail) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    EcCurve curve = EcCurve::CURVE_25519;
+    ErrorCode error = GenerateKey(AuthorizationSetBuilder()
+                                          .Authorization(TAG_NO_AUTH_REQUIRED)
+                                          .EcdsaSigningKey(curve)
+                                          .Digest(Digest::NONE)
+                                          .SetDefaultValidity());
+    ASSERT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
+
+    auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
+
+    for (size_t msg_size : {MAX_ED25519_MSG_SIZE + 1, MAX_ED25519_MSG_SIZE * 2}) {
+        SCOPED_TRACE(testing::Message() << "-msg-size=" << msg_size);
+        string message(msg_size, 'a');
+
+        // Attempt to sign via Begin+Finish.
+        AuthorizationSet out_params;
+        ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params));
+        EXPECT_TRUE(out_params.empty());
+        string signature;
+        auto result = Finish(message, &signature);
+        EXPECT_EQ(result, ErrorCode::INVALID_INPUT_LENGTH);
+
+        // Attempt to sign via Begin+Update (but never get to Finish)
+        ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params));
+        EXPECT_TRUE(out_params.empty());
+        string output;
+        result = Update(message, &output);
+        EXPECT_EQ(result, ErrorCode::INVALID_INPUT_LENGTH);
+    }
+
+    CheckedDeleteKey();
+}
+
+/*
  * SigningOperationsTest.EcdsaNoDigestHugeData
  *
  * Verifies that ECDSA operations support very large messages, even without digesting.  This
@@ -3509,6 +3870,255 @@
 }
 
 /*
+ * ImportKeyTest.Ed25519RawSuccess
+ *
+ * Verifies that importing and using a raw Ed25519 private key works correctly.
+ */
+TEST_P(ImportKeyTest, Ed25519RawSuccess) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .EcdsaSigningKey(EcCurve::CURVE_25519)
+                                               .Digest(Digest::NONE)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::RAW, ed25519_key));
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+    CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519);
+    CheckOrigin();
+
+    // The returned cert should hold the correct public key.
+    ASSERT_GT(cert_chain_.size(), 0);
+    X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+    ASSERT_NE(kmKeyCert, nullptr);
+    EVP_PKEY_Ptr kmPubKey(X509_get_pubkey(kmKeyCert.get()));
+    ASSERT_NE(kmPubKey.get(), nullptr);
+    size_t kmPubKeySize = 32;
+    uint8_t kmPubKeyData[32];
+    ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize));
+    ASSERT_EQ(kmPubKeySize, 32);
+    EXPECT_EQ(string(kmPubKeyData, kmPubKeyData + 32), ed25519_pubkey);
+
+    string message(32, 'a');
+    auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
+    string signature = SignMessage(message, params);
+    LocalVerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.Ed25519Pkcs8Success
+ *
+ * Verifies that importing and using a PKCS#8-encoded Ed25519 private key works correctly.
+ */
+TEST_P(ImportKeyTest, Ed25519Pkcs8Success) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .EcdsaSigningKey(EcCurve::CURVE_25519)
+                                               .Digest(Digest::NONE)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, ed25519_pkcs8_key));
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+    CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519);
+    CheckOrigin();
+
+    // The returned cert should hold the correct public key.
+    ASSERT_GT(cert_chain_.size(), 0);
+    X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+    ASSERT_NE(kmKeyCert, nullptr);
+    EVP_PKEY_Ptr kmPubKey(X509_get_pubkey(kmKeyCert.get()));
+    ASSERT_NE(kmPubKey.get(), nullptr);
+    size_t kmPubKeySize = 32;
+    uint8_t kmPubKeyData[32];
+    ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize));
+    ASSERT_EQ(kmPubKeySize, 32);
+    EXPECT_EQ(string(kmPubKeyData, kmPubKeyData + 32), ed25519_pubkey);
+
+    string message(32, 'a');
+    auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
+    string signature = SignMessage(message, params);
+    LocalVerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.Ed25519CurveMismatch
+ *
+ * Verifies that importing an Ed25519 key pair with a curve that doesn't match the key fails in
+ * the correct way.
+ */
+TEST_P(ImportKeyTest, Ed25519CurveMismatch) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    ASSERT_NE(ErrorCode::OK,
+              ImportKey(AuthorizationSetBuilder()
+                                .EcdsaSigningKey(EcCurve::P_224 /* Doesn't match key */)
+                                .Digest(Digest::NONE)
+                                .SetDefaultValidity(),
+                        KeyFormat::RAW, ed25519_key));
+}
+
+/*
+ * ImportKeyTest.Ed25519FormatMismatch
+ *
+ * Verifies that importing an Ed25519 key pair with an invalid format fails.
+ */
+TEST_P(ImportKeyTest, Ed25519FormatMismatch) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .EcdsaSigningKey(EcCurve::CURVE_25519)
+                                               .Digest(Digest::NONE)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, ed25519_key));
+    ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .EcdsaSigningKey(EcCurve::CURVE_25519)
+                                               .Digest(Digest::NONE)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::RAW, ed25519_pkcs8_key));
+}
+
+/*
+ * ImportKeyTest.Ed25519PurposeMismatch
+ *
+ * Verifies that importing an Ed25519 key pair with an invalid purpose fails.
+ */
+TEST_P(ImportKeyTest, Ed25519PurposeMismatch) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    // Can't have both SIGN and ATTEST_KEY
+    ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .EcdsaSigningKey(EcCurve::CURVE_25519)
+                                               .Authorization(TAG_PURPOSE, KeyPurpose::ATTEST_KEY)
+                                               .Digest(Digest::NONE)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::RAW, ed25519_key));
+    // AGREE_KEY is for X25519 (but can only tell the difference if the import key is in
+    // PKCS#8 format and so includes an OID).
+    ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .EcdsaKey(EcCurve::CURVE_25519)
+                                               .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+                                               .Digest(Digest::NONE)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, ed25519_pkcs8_key));
+}
+
+/*
+ * ImportKeyTest.X25519RawSuccess
+ *
+ * Verifies that importing and using a raw X25519 private key works correctly.
+ */
+TEST_P(ImportKeyTest, X25519RawSuccess) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .EcdsaKey(EcCurve::CURVE_25519)
+                                               .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::RAW, x25519_key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+    CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519);
+    CheckOrigin();
+}
+
+/*
+ * ImportKeyTest.X25519Pkcs8Success
+ *
+ * Verifies that importing and using a PKCS#8-encoded X25519 private key works correctly.
+ */
+TEST_P(ImportKeyTest, X25519Pkcs8Success) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .EcdsaKey(EcCurve::CURVE_25519)
+                                               .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, x25519_pkcs8_key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+    CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519);
+    CheckOrigin();
+}
+
+/*
+ * ImportKeyTest.X25519CurveMismatch
+ *
+ * Verifies that importing an X25519 key with a curve that doesn't match the key fails in
+ * the correct way.
+ */
+TEST_P(ImportKeyTest, X25519CurveMismatch) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .EcdsaKey(EcCurve::P_224 /* Doesn't match key */)
+                                               .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::RAW, x25519_key));
+}
+
+/*
+ * ImportKeyTest.X25519FormatMismatch
+ *
+ * Verifies that importing an X25519 key with an invalid format fails.
+ */
+TEST_P(ImportKeyTest, X25519FormatMismatch) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .EcdsaKey(EcCurve::CURVE_25519)
+                                               .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, x25519_key));
+    ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .EcdsaKey(EcCurve::CURVE_25519)
+                                               .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::RAW, x25519_pkcs8_key));
+}
+
+/*
+ * ImportKeyTest.X25519PurposeMismatch
+ *
+ * Verifies that importing an X25519 key pair with an invalid format fails.
+ */
+TEST_P(ImportKeyTest, X25519PurposeMismatch) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .EcdsaKey(EcCurve::CURVE_25519)
+                                               .Authorization(TAG_PURPOSE, KeyPurpose::ATTEST_KEY)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, x25519_pkcs8_key));
+    ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .EcdsaSigningKey(EcCurve::CURVE_25519)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, x25519_pkcs8_key));
+}
+
+/*
  * ImportKeyTest.AesSuccess
  *
  * Verifies that importing and using an AES key works.
@@ -6660,8 +7270,6 @@
 
 INSTANTIATE_KEYMINT_AIDL_TEST(TransportLimitTest);
 
-typedef KeyMintAidlTestBase KeyAgreementTest;
-
 static int EcdhCurveToOpenSslCurveName(EcCurve curve) {
     switch (curve) {
         case EcCurve::P_224:
@@ -6677,10 +7285,108 @@
     }
 }
 
+class KeyAgreementTest : public KeyMintAidlTestBase {
+  protected:
+    void GenerateLocalEcKey(EcCurve localCurve, EVP_PKEY_Ptr* localPrivKey,
+                            std::vector<uint8_t>* localPublicKey) {
+        // Generate EC key locally (with access to private key material)
+        if (localCurve == EcCurve::CURVE_25519) {
+            uint8_t privKeyData[32];
+            uint8_t pubKeyData[32];
+            X25519_keypair(pubKeyData, privKeyData);
+            *localPublicKey = vector<uint8_t>(pubKeyData, pubKeyData + 32);
+            *localPrivKey = EVP_PKEY_Ptr(EVP_PKEY_new_raw_private_key(
+                    EVP_PKEY_X25519, nullptr, privKeyData, sizeof(privKeyData)));
+        } else {
+            auto ecKey = EC_KEY_Ptr(EC_KEY_new());
+            int curveName = EcdhCurveToOpenSslCurveName(localCurve);
+            auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(curveName));
+            ASSERT_NE(group, nullptr);
+            ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1);
+            ASSERT_EQ(EC_KEY_generate_key(ecKey.get()), 1);
+            *localPrivKey = EVP_PKEY_Ptr(EVP_PKEY_new());
+            ASSERT_EQ(EVP_PKEY_set1_EC_KEY(localPrivKey->get(), ecKey.get()), 1);
+
+            // Get encoded form of the public part of the locally generated key...
+            unsigned char* p = nullptr;
+            int localPublicKeySize = i2d_PUBKEY(localPrivKey->get(), &p);
+            ASSERT_GT(localPublicKeySize, 0);
+            *localPublicKey =
+                    vector<uint8_t>(reinterpret_cast<const uint8_t*>(p),
+                                    reinterpret_cast<const uint8_t*>(p + localPublicKeySize));
+            OPENSSL_free(p);
+        }
+    }
+
+    void GenerateKeyMintEcKey(EcCurve curve, EVP_PKEY_Ptr* kmPubKey) {
+        vector<uint8_t> challenge = {0x41, 0x42};
+        ErrorCode result =
+                GenerateKey(AuthorizationSetBuilder()
+                                    .Authorization(TAG_NO_AUTH_REQUIRED)
+                                    .Authorization(TAG_EC_CURVE, curve)
+                                    .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+                                    .Authorization(TAG_ALGORITHM, Algorithm::EC)
+                                    .Authorization(TAG_ATTESTATION_APPLICATION_ID, {0x61, 0x62})
+                                    .Authorization(TAG_ATTESTATION_CHALLENGE, challenge)
+                                    .SetDefaultValidity());
+        ASSERT_EQ(ErrorCode::OK, result) << "Failed to generate key";
+        ASSERT_GT(cert_chain_.size(), 0);
+        X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+        ASSERT_NE(kmKeyCert, nullptr);
+        // Check that keyAgreement (bit 4) is set in KeyUsage
+        EXPECT_TRUE((X509_get_key_usage(kmKeyCert.get()) & X509v3_KU_KEY_AGREEMENT) != 0);
+        *kmPubKey = EVP_PKEY_Ptr(X509_get_pubkey(kmKeyCert.get()));
+        ASSERT_NE(*kmPubKey, nullptr);
+        if (dump_Attestations) {
+            for (size_t n = 0; n < cert_chain_.size(); n++) {
+                std::cout << bin2hex(cert_chain_[n].encodedCertificate) << std::endl;
+            }
+        }
+    }
+
+    void CheckAgreement(EVP_PKEY_Ptr kmPubKey, EVP_PKEY_Ptr localPrivKey,
+                        const std::vector<uint8_t>& localPublicKey) {
+        ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
+        string ZabFromKeyMintStr;
+        ASSERT_EQ(ErrorCode::OK,
+                  Finish(string(localPublicKey.begin(), localPublicKey.end()), &ZabFromKeyMintStr));
+        vector<uint8_t> ZabFromKeyMint(ZabFromKeyMintStr.begin(), ZabFromKeyMintStr.end());
+        vector<uint8_t> ZabFromTest;
+
+        if (EVP_PKEY_id(kmPubKey.get()) == EVP_PKEY_X25519) {
+            size_t kmPubKeySize = 32;
+            uint8_t kmPubKeyData[32];
+            ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize));
+            ASSERT_EQ(kmPubKeySize, 32);
+
+            uint8_t localPrivKeyData[32];
+            size_t localPrivKeySize = 32;
+            ASSERT_EQ(1, EVP_PKEY_get_raw_private_key(localPrivKey.get(), localPrivKeyData,
+                                                      &localPrivKeySize));
+            ASSERT_EQ(localPrivKeySize, 32);
+
+            uint8_t sharedKey[32];
+            ASSERT_EQ(1, X25519(sharedKey, localPrivKeyData, kmPubKeyData));
+            ZabFromTest = std::vector<uint8_t>(sharedKey, sharedKey + 32);
+        } else {
+            // Perform local ECDH between the two keys so we can check if we get the same Zab..
+            auto ctx = EVP_PKEY_CTX_Ptr(EVP_PKEY_CTX_new(localPrivKey.get(), nullptr));
+            ASSERT_NE(ctx, nullptr);
+            ASSERT_EQ(EVP_PKEY_derive_init(ctx.get()), 1);
+            ASSERT_EQ(EVP_PKEY_derive_set_peer(ctx.get(), kmPubKey.get()), 1);
+            size_t ZabFromTestLen = 0;
+            ASSERT_EQ(EVP_PKEY_derive(ctx.get(), nullptr, &ZabFromTestLen), 1);
+            ZabFromTest.resize(ZabFromTestLen);
+            ASSERT_EQ(EVP_PKEY_derive(ctx.get(), ZabFromTest.data(), &ZabFromTestLen), 1);
+        }
+        EXPECT_EQ(ZabFromKeyMint, ZabFromTest);
+    }
+};
+
 /*
  * KeyAgreementTest.Ecdh
  *
- * Verifies that ECDH works for all curves
+ * Verifies that ECDH works for all required curves
  */
 TEST_P(KeyAgreementTest, Ecdh) {
     // Because it's possible to use this API with keys on different curves, we
@@ -6694,49 +7400,13 @@
     for (auto curve : ValidCurves()) {
         for (auto localCurve : ValidCurves()) {
             // Generate EC key locally (with access to private key material)
-            auto ecKey = EC_KEY_Ptr(EC_KEY_new());
-            int curveName = EcdhCurveToOpenSslCurveName(localCurve);
-            auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(curveName));
-            ASSERT_NE(group, nullptr);
-            ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1);
-            ASSERT_EQ(EC_KEY_generate_key(ecKey.get()), 1);
-            auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
-            ASSERT_EQ(EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()), 1);
-
-            // Get encoded form of the public part of the locally generated key...
-            unsigned char* p = nullptr;
-            int encodedPublicKeySize = i2d_PUBKEY(pkey.get(), &p);
-            ASSERT_GT(encodedPublicKeySize, 0);
-            vector<uint8_t> encodedPublicKey(
-                    reinterpret_cast<const uint8_t*>(p),
-                    reinterpret_cast<const uint8_t*>(p + encodedPublicKeySize));
-            OPENSSL_free(p);
+            EVP_PKEY_Ptr localPrivKey;
+            vector<uint8_t> localPublicKey;
+            GenerateLocalEcKey(localCurve, &localPrivKey, &localPublicKey);
 
             // Generate EC key in KeyMint (only access to public key material)
-            vector<uint8_t> challenge = {0x41, 0x42};
-            EXPECT_EQ(
-                    ErrorCode::OK,
-                    GenerateKey(AuthorizationSetBuilder()
-                                        .Authorization(TAG_NO_AUTH_REQUIRED)
-                                        .Authorization(TAG_EC_CURVE, curve)
-                                        .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
-                                        .Authorization(TAG_ALGORITHM, Algorithm::EC)
-                                        .Authorization(TAG_ATTESTATION_APPLICATION_ID, {0x61, 0x62})
-                                        .Authorization(TAG_ATTESTATION_CHALLENGE, challenge)
-                                        .SetDefaultValidity()))
-                    << "Failed to generate key";
-            ASSERT_GT(cert_chain_.size(), 0);
-            X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
-            ASSERT_NE(kmKeyCert, nullptr);
-            // Check that keyAgreement (bit 4) is set in KeyUsage
-            EXPECT_TRUE((X509_get_key_usage(kmKeyCert.get()) & X509v3_KU_KEY_AGREEMENT) != 0);
-            auto kmPkey = EVP_PKEY_Ptr(X509_get_pubkey(kmKeyCert.get()));
-            ASSERT_NE(kmPkey, nullptr);
-            if (dump_Attestations) {
-                for (size_t n = 0; n < cert_chain_.size(); n++) {
-                    std::cout << bin2hex(cert_chain_[n].encodedCertificate) << std::endl;
-                }
-            }
+            EVP_PKEY_Ptr kmPubKey;
+            GenerateKeyMintEcKey(curve, &kmPubKey);
 
             // Now that we have the two keys, we ask KeyMint to perform ECDH...
             if (curve != localCurve) {
@@ -6745,30 +7415,12 @@
                 EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
                 string ZabFromKeyMintStr;
                 EXPECT_EQ(ErrorCode::INVALID_ARGUMENT,
-                          Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()),
+                          Finish(string(localPublicKey.begin(), localPublicKey.end()),
                                  &ZabFromKeyMintStr));
 
             } else {
                 // Otherwise if the keys are using the same curve, it should work.
-                EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
-                string ZabFromKeyMintStr;
-                EXPECT_EQ(ErrorCode::OK,
-                          Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()),
-                                 &ZabFromKeyMintStr));
-                vector<uint8_t> ZabFromKeyMint(ZabFromKeyMintStr.begin(), ZabFromKeyMintStr.end());
-
-                // Perform local ECDH between the two keys so we can check if we get the same Zab..
-                auto ctx = EVP_PKEY_CTX_Ptr(EVP_PKEY_CTX_new(pkey.get(), nullptr));
-                ASSERT_NE(ctx, nullptr);
-                ASSERT_EQ(EVP_PKEY_derive_init(ctx.get()), 1);
-                ASSERT_EQ(EVP_PKEY_derive_set_peer(ctx.get(), kmPkey.get()), 1);
-                size_t ZabFromTestLen = 0;
-                ASSERT_EQ(EVP_PKEY_derive(ctx.get(), nullptr, &ZabFromTestLen), 1);
-                vector<uint8_t> ZabFromTest;
-                ZabFromTest.resize(ZabFromTestLen);
-                ASSERT_EQ(EVP_PKEY_derive(ctx.get(), ZabFromTest.data(), &ZabFromTestLen), 1);
-
-                EXPECT_EQ(ZabFromKeyMint, ZabFromTest);
+                CheckAgreement(std::move(kmPubKey), std::move(localPrivKey), localPublicKey);
             }
 
             CheckedDeleteKey();
@@ -6776,6 +7428,140 @@
     }
 }
 
+/*
+ * KeyAgreementTest.EcdhCurve25519
+ *
+ * Verifies that ECDH works for curve25519. This is also covered by the general
+ * KeyAgreementTest.Ecdh case, but is pulled out separately here because this curve was added after
+ * KeyMint 1.0.
+ */
+TEST_P(KeyAgreementTest, EcdhCurve25519) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    // Generate EC key in KeyMint (only access to public key material)
+    EcCurve curve = EcCurve::CURVE_25519;
+    EVP_PKEY_Ptr kmPubKey = nullptr;
+    GenerateKeyMintEcKey(curve, &kmPubKey);
+
+    // Generate EC key on same curve locally (with access to private key material).
+    EVP_PKEY_Ptr privKey;
+    vector<uint8_t> encodedPublicKey;
+    GenerateLocalEcKey(curve, &privKey, &encodedPublicKey);
+
+    // Agree on a key between local and KeyMint and check it.
+    CheckAgreement(std::move(kmPubKey), std::move(privKey), encodedPublicKey);
+
+    CheckedDeleteKey();
+}
+
+/*
+ * KeyAgreementTest.EcdhCurve25519Imported
+ *
+ * Verifies that ECDH works for an imported curve25519 key.
+ */
+TEST_P(KeyAgreementTest, EcdhCurve25519Imported) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    // Import x25519 key into KeyMint.
+    EcCurve curve = EcCurve::CURVE_25519;
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .EcdsaKey(EcCurve::CURVE_25519)
+                                               .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, x25519_pkcs8_key));
+    ASSERT_GT(cert_chain_.size(), 0);
+    X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+    ASSERT_NE(kmKeyCert, nullptr);
+    EVP_PKEY_Ptr kmPubKey(X509_get_pubkey(kmKeyCert.get()));
+    ASSERT_NE(kmPubKey.get(), nullptr);
+
+    // Expect the import to emit corresponding public key data.
+    size_t kmPubKeySize = 32;
+    uint8_t kmPubKeyData[32];
+    ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize));
+    ASSERT_EQ(kmPubKeySize, 32);
+    EXPECT_EQ(bin2hex(std::vector<uint8_t>(kmPubKeyData, kmPubKeyData + 32)),
+              bin2hex(std::vector<uint8_t>(x25519_pubkey.begin(), x25519_pubkey.end())));
+
+    // Generate EC key on same curve locally (with access to private key material).
+    EVP_PKEY_Ptr privKey;
+    vector<uint8_t> encodedPublicKey;
+    GenerateLocalEcKey(curve, &privKey, &encodedPublicKey);
+
+    // Agree on a key between local and KeyMint and check it.
+    CheckAgreement(std::move(kmPubKey), std::move(privKey), encodedPublicKey);
+
+    CheckedDeleteKey();
+}
+
+/*
+ * KeyAgreementTest.EcdhCurve25519InvalidSize
+ *
+ * Verifies that ECDH fails for curve25519 if the wrong size of public key is provided.
+ */
+TEST_P(KeyAgreementTest, EcdhCurve25519InvalidSize) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    // Generate EC key in KeyMint (only access to public key material)
+    EcCurve curve = EcCurve::CURVE_25519;
+    EVP_PKEY_Ptr kmPubKey = nullptr;
+    GenerateKeyMintEcKey(curve, &kmPubKey);
+
+    // Generate EC key on same curve locally (with access to private key material).
+    EVP_PKEY_Ptr privKey;
+    vector<uint8_t> encodedPublicKey;
+    GenerateLocalEcKey(curve, &privKey, &encodedPublicKey);
+
+    ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
+    string ZabFromKeyMintStr;
+    // Send in an incomplete public key.
+    ASSERT_NE(ErrorCode::OK, Finish(string(encodedPublicKey.begin(), encodedPublicKey.end() - 1),
+                                    &ZabFromKeyMintStr));
+
+    CheckedDeleteKey();
+}
+
+/*
+ * KeyAgreementTest.EcdhCurve25519Mismatch
+ *
+ * Verifies that ECDH fails between curve25519 and other curves.
+ */
+TEST_P(KeyAgreementTest, EcdhCurve25519Mismatch) {
+    if (!Curve25519Supported()) {
+        GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+    }
+
+    // Generate EC key in KeyMint (only access to public key material)
+    EcCurve curve = EcCurve::CURVE_25519;
+    EVP_PKEY_Ptr kmPubKey = nullptr;
+    GenerateKeyMintEcKey(curve, &kmPubKey);
+
+    for (auto localCurve : ValidCurves()) {
+        if (localCurve == curve) {
+            continue;
+        }
+        // Generate EC key on a different curve locally (with access to private key material).
+        EVP_PKEY_Ptr privKey;
+        vector<uint8_t> encodedPublicKey;
+        GenerateLocalEcKey(localCurve, &privKey, &encodedPublicKey);
+
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
+        string ZabFromKeyMintStr;
+        EXPECT_EQ(ErrorCode::INVALID_ARGUMENT,
+                  Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()),
+                         &ZabFromKeyMintStr));
+    }
+
+    CheckedDeleteKey();
+}
+
 INSTANTIATE_KEYMINT_AIDL_TEST(KeyAgreementTest);
 
 using DestroyAttestationIdsTest = KeyMintAidlTestBase;
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index c9d506f..829780d 100644
--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -20,6 +20,7 @@
 #include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
 #include <aidl/android/hardware/security/keymint/SecurityLevel.h>
 #include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
 #include <cppbor_parse.h>
 #include <gmock/gmock.h>
 #include <keymaster/cppcose/cppcose.h>
@@ -29,6 +30,7 @@
 #include <openssl/ec_key.h>
 #include <openssl/x509.h>
 #include <remote_prov/remote_prov_utils.h>
+#include <set>
 #include <vector>
 
 #include "KeyMintAidlTestBase.h"
@@ -40,6 +42,8 @@
 
 namespace {
 
+constexpr int32_t VERSION_WITH_UNIQUE_ID_SUPPORT = 2;
+
 #define INSTANTIATE_REM_PROV_AIDL_TEST(name)                                         \
     GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name);                             \
     INSTANTIATE_TEST_SUITE_P(                                                        \
@@ -47,6 +51,7 @@
             testing::ValuesIn(VtsRemotelyProvisionedComponentTests::build_params()), \
             ::android::PrintInstanceNameToString)
 
+using ::android::sp;
 using bytevec = std::vector<uint8_t>;
 using testing::MatchesRegex;
 using namespace remote_prov;
@@ -175,6 +180,67 @@
     std::shared_ptr<IRemotelyProvisionedComponent> provisionable_;
 };
 
+/**
+ * Verify that every implementation reports a different unique id.
+ */
+TEST(NonParameterizedTests, eachRpcHasAUniqueId) {
+    std::set<std::string> uniqueIds;
+    for (auto hal : ::android::getAidlHalInstanceNames(IRemotelyProvisionedComponent::descriptor)) {
+        ASSERT_TRUE(AServiceManager_isDeclared(hal.c_str()));
+        ::ndk::SpAIBinder binder(AServiceManager_waitForService(hal.c_str()));
+        std::shared_ptr<IRemotelyProvisionedComponent> rpc =
+                IRemotelyProvisionedComponent::fromBinder(binder);
+        ASSERT_NE(rpc, nullptr);
+
+        RpcHardwareInfo hwInfo;
+        ASSERT_TRUE(rpc->getHardwareInfo(&hwInfo).isOk());
+
+        int32_t version;
+        ASSERT_TRUE(rpc->getInterfaceVersion(&version).isOk());
+        if (version >= VERSION_WITH_UNIQUE_ID_SUPPORT) {
+            ASSERT_TRUE(hwInfo.uniqueId);
+            auto [_, wasInserted] = uniqueIds.insert(*hwInfo.uniqueId);
+            EXPECT_TRUE(wasInserted);
+        } else {
+            ASSERT_FALSE(hwInfo.uniqueId);
+        }
+    }
+}
+
+using GetHardwareInfoTests = VtsRemotelyProvisionedComponentTests;
+
+INSTANTIATE_REM_PROV_AIDL_TEST(GetHardwareInfoTests);
+
+/**
+ * Verify that a valid curve is reported by the implementation.
+ */
+TEST_P(GetHardwareInfoTests, supportsValidCurve) {
+    RpcHardwareInfo hwInfo;
+    ASSERT_TRUE(provisionable_->getHardwareInfo(&hwInfo).isOk());
+
+    const std::set<int> validCurves = {RpcHardwareInfo::CURVE_P256, RpcHardwareInfo::CURVE_25519};
+    ASSERT_EQ(validCurves.count(hwInfo.supportedEekCurve), 1)
+            << "Invalid curve: " << hwInfo.supportedEekCurve;
+}
+
+/**
+ * Verify that the unique id is within the length limits as described in RpcHardwareInfo.aidl.
+ */
+TEST_P(GetHardwareInfoTests, uniqueId) {
+    int32_t version;
+    ASSERT_TRUE(provisionable_->getInterfaceVersion(&version).isOk());
+
+    if (version < VERSION_WITH_UNIQUE_ID_SUPPORT) {
+        return;
+    }
+
+    RpcHardwareInfo hwInfo;
+    ASSERT_TRUE(provisionable_->getHardwareInfo(&hwInfo).isOk());
+    ASSERT_TRUE(hwInfo.uniqueId);
+    EXPECT_GE(hwInfo.uniqueId->size(), 1);
+    EXPECT_LE(hwInfo.uniqueId->size(), 32);
+}
+
 using GenerateKeyTests = VtsRemotelyProvisionedComponentTests;
 
 INSTANTIATE_REM_PROV_AIDL_TEST(GenerateKeyTests);