Merge "KeyMint: sync all attestation tags"
diff --git a/bluetooth/audio/2.1/default/Android.bp b/bluetooth/audio/2.1/default/Android.bp
index 5c30f79..3000223 100644
--- a/bluetooth/audio/2.1/default/Android.bp
+++ b/bluetooth/audio/2.1/default/Android.bp
@@ -19,6 +19,7 @@
         "A2dpSoftwareAudioProvider.cpp",
         "HearingAidAudioProvider.cpp",
         "LeAudioAudioProvider.cpp",
+        "LeAudioOffloadAudioProvider.cpp",
     ],
     header_libs: ["libhardware_headers"],
     shared_libs: [
diff --git a/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.cpp b/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.cpp
index e1b1ac6..b0d171a 100644
--- a/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.cpp
+++ b/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.cpp
@@ -41,8 +41,12 @@
     BluetoothAudioProvidersFactory::hearing_aid_provider_instance_;
 LeAudioOutputAudioProvider
     BluetoothAudioProvidersFactory::leaudio_output_provider_instance_;
+LeAudioOffloadOutputAudioProvider
+    BluetoothAudioProvidersFactory::leaudio_offload_output_provider_instance_;
 LeAudioInputAudioProvider
     BluetoothAudioProvidersFactory::leaudio_input_provider_instance_;
+LeAudioOffloadInputAudioProvider
+    BluetoothAudioProvidersFactory::leaudio_offload_input_provider_instance_;
 
 Return<void> BluetoothAudioProvidersFactory::openProvider(
     const V2_0::SessionType sessionType, openProvider_cb _hidl_cb) {
@@ -90,9 +94,15 @@
     case SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH:
       provider = &leaudio_output_provider_instance_;
       break;
+    case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
+      provider = &leaudio_offload_output_provider_instance_;
+      break;
     case SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH:
       provider = &leaudio_input_provider_instance_;
       break;
+    case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
+      provider = &leaudio_offload_input_provider_instance_;
+      break;
     default:
       status = BluetoothAudioStatus::FAILURE;
   }
diff --git a/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.h b/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.h
index fd83694..f8f557e 100644
--- a/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.h
+++ b/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.h
@@ -23,6 +23,7 @@
 #include "BluetoothAudioProvider.h"
 #include "HearingAidAudioProvider.h"
 #include "LeAudioAudioProvider.h"
+#include "LeAudioOffloadAudioProvider.h"
 
 namespace android {
 namespace hardware {
@@ -55,6 +56,8 @@
   static HearingAidAudioProvider hearing_aid_provider_instance_;
   static LeAudioOutputAudioProvider leaudio_output_provider_instance_;
   static LeAudioInputAudioProvider leaudio_input_provider_instance_;
+  static LeAudioOffloadOutputAudioProvider leaudio_offload_output_provider_instance_;
+  static LeAudioOffloadInputAudioProvider leaudio_offload_input_provider_instance_;
 };
 
 extern "C" IBluetoothAudioProvidersFactory*
diff --git a/bluetooth/audio/2.1/default/LeAudioOffloadAudioProvider.cpp b/bluetooth/audio/2.1/default/LeAudioOffloadAudioProvider.cpp
new file mode 100644
index 0000000..c11bdad
--- /dev/null
+++ b/bluetooth/audio/2.1/default/LeAudioOffloadAudioProvider.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProviderLeAudioOffload"
+
+#include "LeAudioOffloadAudioProvider.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport_2_1.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_1::SampleRate;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+LeAudioOffloadOutputAudioProvider::LeAudioOffloadOutputAudioProvider()
+    : LeAudioOffloadAudioProvider() {
+  session_type_ = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
+}
+
+LeAudioOffloadInputAudioProvider::LeAudioOffloadInputAudioProvider()
+    : LeAudioOffloadAudioProvider() {
+  session_type_ = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH;
+}
+
+LeAudioOffloadAudioProvider::LeAudioOffloadAudioProvider()
+    : BluetoothAudioProvider() {}
+
+bool LeAudioOffloadAudioProvider::isValid(const V2_0::SessionType& sessionType) {
+  LOG(ERROR) << __func__ << ", invalid session type for Offloaded Le Audio provider: "
+             << toString(sessionType);
+
+  return false;
+}
+
+bool LeAudioOffloadAudioProvider::isValid(const SessionType& sessionType) {
+  return (sessionType == session_type_);
+}
+
+Return<void> LeAudioOffloadAudioProvider::startSession_2_1(
+    const sp<V2_0::IBluetoothAudioPort>& hostIf,
+    const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+  /**
+   * Initialize the audio platform if audioConfiguration is supported.
+   * Save the IBluetoothAudioPort interface, so that it can be used
+   * later to send stream control commands to the HAL client, based on
+   * interaction with Audio framework.
+   */
+  if (audioConfig.getDiscriminator() !=
+      AudioConfiguration::hidl_discriminator::leAudioCodecConfig) {
+    LOG(WARNING) << __func__
+                 << " - Invalid Audio Configuration=" << toString(audioConfig);
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  }
+
+  if (!android::bluetooth::audio::IsOffloadLeAudioConfigurationValid(session_type_,
+                 audioConfig.leAudioCodecConfig())) {
+    LOG(WARNING) << __func__ << " - Unsupported LC3 Offloaded Configuration="
+                 << toString(audioConfig.leAudioCodecConfig());
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  }
+
+  return BluetoothAudioProvider::startSession_2_1(hostIf, audioConfig,
+                                                  _hidl_cb);
+}
+
+Return<void> LeAudioOffloadAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
+  BluetoothAudioSessionReport_2_1::OnSessionStarted(session_type_, stack_iface_,
+                                                    nullptr, audio_config_);
+  _hidl_cb(BluetoothAudioStatus::SUCCESS, DataMQ::Descriptor());
+  return Void();
+}
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.1/default/LeAudioOffloadAudioProvider.h b/bluetooth/audio/2.1/default/LeAudioOffloadAudioProvider.h
new file mode 100644
index 0000000..564e9a3
--- /dev/null
+++ b/bluetooth/audio/2.1/default/LeAudioOffloadAudioProvider.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.1/types.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
+ public:
+  LeAudioOffloadAudioProvider();
+
+  bool isValid(const SessionType& sessionType) override;
+  bool isValid(const V2_0::SessionType& sessionType) override;
+
+  Return<void> startSession_2_1(const sp<V2_0::IBluetoothAudioPort>& hostIf,
+                                const AudioConfiguration& audioConfig,
+                                startSession_cb _hidl_cb) override;
+
+ private:
+  Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider {
+ public:
+  LeAudioOffloadOutputAudioProvider();
+};
+
+class LeAudioOffloadInputAudioProvider : public LeAudioOffloadAudioProvider {
+ public:
+  LeAudioOffloadInputAudioProvider();
+};
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession_2_1.cpp b/bluetooth/audio/utils/session/BluetoothAudioSession_2_1.cpp
index 9d91196..3228a09 100644
--- a/bluetooth/audio/utils/session/BluetoothAudioSession_2_1.cpp
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_1.cpp
@@ -110,20 +110,29 @@
            SessionType_2_1::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
        session_type_2_1_ ==
            SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH);
-  bool is_offload_session =
+  bool is_offload_a2dp_session =
       (session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  bool is_offload_le_audio_session =
+      (session_type_2_1_ == SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
+       session_type_2_1_ == SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH);
   auto audio_config_discriminator = audio_config.getDiscriminator();
   bool is_software_audio_config =
       (is_software_session &&
        audio_config_discriminator ==
            ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
                hidl_discriminator::pcmConfig);
-  bool is_offload_audio_config =
-      (is_offload_session &&
+  bool is_a2dp_offload_audio_config =
+      (is_offload_a2dp_session &&
        audio_config_discriminator ==
            ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
                hidl_discriminator::codecConfig);
-  if (!is_software_audio_config && !is_offload_audio_config) {
+  bool is_le_audio_offload_audio_config =
+      (is_offload_le_audio_session &&
+       audio_config_discriminator ==
+           ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
+               hidl_discriminator::leAudioCodecConfig);
+  if (!is_software_audio_config && !is_a2dp_offload_audio_config &&
+      !is_le_audio_offload_audio_config) {
     return false;
   }
   audio_config_2_1_ = audio_config;
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.cpp b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.cpp
index 8b0b0f7..c90ce6d 100644
--- a/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.cpp
+++ b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.cpp
@@ -122,6 +122,21 @@
   return false;
 }
 
+bool IsOffloadLeAudioConfigurationValid(
+    const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+        session_type,
+    const ::android::hardware::bluetooth::audio::V2_1::Lc3CodecConfiguration&) {
+
+  if (session_type != SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
+      session_type != SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
+    return false;
+  }
+
+  //TODO: perform checks on le_audio_codec_config once we know supported parameters
+
+  return true;
+}
+
 }  // namespace audio
 }  // namespace bluetooth
 }  // namespace android
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.h b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.h
index 746d9c0..a52636c 100644
--- a/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.h
+++ b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.h
@@ -41,6 +41,11 @@
     const ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration&
         codec_config);
 
+bool IsOffloadLeAudioConfigurationValid(
+    const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+        session_type,
+    const ::android::hardware::bluetooth::audio::V2_1::Lc3CodecConfiguration&
+        le_audio_codec_config);
 }  // namespace audio
 }  // namespace bluetooth
 }  // namespace android
diff --git a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
index 362ab41..615fde0 100644
--- a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
+++ b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
@@ -496,10 +496,26 @@
  *  - program changes exactly to what was requested.
  */
 TEST_P(BroadcastRadioHalTest, DabTune) {
+    Result halResult;
+    hidl_vec<DabTableEntry> config;
+    auto cb = [&](Result result, hidl_vec<DabTableEntry> configCb) {
+        halResult = result;
+        config = configCb;
+    };
+    auto hidlResult = mModule->getDabRegionConfig(cb);
+    ASSERT_TRUE(hidlResult.isOk());
+
+    if (halResult == Result::NOT_SUPPORTED) {
+        printSkipped("DAB not supported");
+        return;
+    }
+    ASSERT_EQ(Result::OK, halResult);
+    ASSERT_NE(config.size(), 0U);
+
     ASSERT_TRUE(openSession());
 
     ProgramSelector sel = {};
-    uint64_t freq = 178352;
+    uint64_t freq = config[config.size() / 2].frequency;
     sel.primaryId = make_identifier(IdentifierType::DAB_FREQUENCY,freq);
 
     std::this_thread::sleep_for(gTuneWorkaround);
diff --git a/neuralnetworks/aidl/utils/src/Burst.cpp b/neuralnetworks/aidl/utils/src/Burst.cpp
index 87cd0e4..800ac32 100644
--- a/neuralnetworks/aidl/utils/src/Burst.cpp
+++ b/neuralnetworks/aidl/utils/src/Burst.cpp
@@ -62,7 +62,7 @@
   private:
     const std::shared_ptr<const Burst> kBurst;
     const Request kRequest;
-    const std::vector<int64_t>& kMemoryIdentifierTokens;
+    const std::vector<int64_t> kMemoryIdentifierTokens;
     const bool kMeasure;
     const int64_t kLoopTimeoutDuration;
     const hal::utils::RequestRelocation kRelocation;
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
index d3b041d..2356ff0 100644
--- a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
@@ -435,8 +435,8 @@
         mInputMemory = TestBlobAHWB::create(std::max<size_t>(inputSize, 1));
         mOutputMemory = TestBlobAHWB::create(std::max<size_t>(outputSize, 1));
     } else {
-        mInputMemory = TestAshmem::create(std::max<size_t>(inputSize, 1));
-        mOutputMemory = TestAshmem::create(std::max<size_t>(outputSize, 1));
+        mInputMemory = TestAshmem::create(std::max<size_t>(inputSize, 1), /*aidlReadonly=*/true);
+        mOutputMemory = TestAshmem::create(std::max<size_t>(outputSize, 1), /*aidlReadonly=*/false);
     }
     CHECK_NE(mInputMemory, nullptr);
     CHECK_NE(mOutputMemory, nullptr);
diff --git a/neuralnetworks/aidl/vts/functional/Utils.cpp b/neuralnetworks/aidl/vts/functional/Utils.cpp
index 3c7f5f7..9af362e 100644
--- a/neuralnetworks/aidl/vts/functional/Utils.cpp
+++ b/neuralnetworks/aidl/vts/functional/Utils.cpp
@@ -23,6 +23,7 @@
 #include <android/binder_status.h>
 #include <android/hardware_buffer.h>
 
+#include <sys/mman.h>
 #include <iostream>
 #include <limits>
 #include <numeric>
@@ -98,19 +99,39 @@
                            std::multiplies<>{});
 }
 
-std::unique_ptr<TestAshmem> TestAshmem::create(uint32_t size) {
-    auto ashmem = std::make_unique<TestAshmem>(size);
+std::unique_ptr<TestAshmem> TestAshmem::create(uint32_t size, bool aidlReadonly) {
+    auto ashmem = std::make_unique<TestAshmem>(size, aidlReadonly);
     return ashmem->mIsValid ? std::move(ashmem) : nullptr;
 }
 
-void TestAshmem::initialize(uint32_t size) {
+// This function will create a readonly shared memory with PROT_READ only.
+// The input shared memory must be either Ashmem or mapped-FD.
+static nn::SharedMemory convertSharedMemoryToReadonly(const nn::SharedMemory& sharedMemory) {
+    if (std::holds_alternative<nn::Memory::Ashmem>(sharedMemory->handle)) {
+        const auto& memory = std::get<nn::Memory::Ashmem>(sharedMemory->handle);
+        return nn::createSharedMemoryFromFd(memory.size, PROT_READ, memory.fd.get(), /*offset=*/0)
+                .value();
+    } else if (std::holds_alternative<nn::Memory::Fd>(sharedMemory->handle)) {
+        const auto& memory = std::get<nn::Memory::Fd>(sharedMemory->handle);
+        return nn::createSharedMemoryFromFd(memory.size, PROT_READ, memory.fd.get(), memory.offset)
+                .value();
+    }
+    CHECK(false) << "Unexpected shared memory type";
+    return sharedMemory;
+}
+
+void TestAshmem::initialize(uint32_t size, bool aidlReadonly) {
     mIsValid = false;
     ASSERT_GT(size, 0);
     const auto sharedMemory = nn::createSharedMemory(size).value();
     mMappedMemory = nn::map(sharedMemory).value();
     mPtr = static_cast<uint8_t*>(std::get<void*>(mMappedMemory.pointer));
     CHECK_NE(mPtr, nullptr);
-    mAidlMemory = utils::convert(sharedMemory).value();
+    if (aidlReadonly) {
+        mAidlMemory = utils::convert(convertSharedMemoryToReadonly(sharedMemory)).value();
+    } else {
+        mAidlMemory = utils::convert(sharedMemory).value();
+    }
     mIsValid = true;
 }
 
diff --git a/neuralnetworks/aidl/vts/functional/Utils.h b/neuralnetworks/aidl/vts/functional/Utils.h
index 77085a7..9dd7359 100644
--- a/neuralnetworks/aidl/vts/functional/Utils.h
+++ b/neuralnetworks/aidl/vts/functional/Utils.h
@@ -79,15 +79,18 @@
 
 class TestAshmem : public TestMemoryBase {
   public:
-    static std::unique_ptr<TestAshmem> create(uint32_t size);
+    // If aidlReadonly is true, getAidlMemory will return a sAIDL memory with readonly access;
+    // otherwise, the sAIDL memory has read-write access. This only affects the sAIDL memory.
+    // getPointer will always return a valid address with read-write access.
+    static std::unique_ptr<TestAshmem> create(uint32_t size, bool aidlReadonly = false);
 
     // Prefer TestAshmem::create.
     // The constructor calls initialize, which constructs the memory resources. This is a workaround
     // that gtest macros cannot be used directly in a constructor.
-    TestAshmem(uint32_t size) { initialize(size); }
+    TestAshmem(uint32_t size, bool aidlReadonly) { initialize(size, aidlReadonly); }
 
   private:
-    void initialize(uint32_t size);
+    void initialize(uint32_t size, bool aidlReadonly);
     nn::Mapping mMappedMemory;
 };
 
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
index 422e91c..34395ca 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
@@ -821,7 +821,7 @@
 /*
  * Test IRadio.updateSimPhonebookRecords() for the response returned.
  */
-TEST_F(RadioHidlTest_v1_6, updateSimPhonebookRecords) {
+TEST_P(RadioHidlTest_v1_6, updateSimPhonebookRecords) {
     serial = GetRandomSerialNumber();
     radio_v1_6->getSimPhonebookCapacity(serial);
     EXPECT_EQ(std::cv_status::no_timeout, wait());
@@ -851,7 +851,11 @@
         EXPECT_EQ(std::cv_status::no_timeout, wait());
         EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
         EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
-        EXPECT_EQ(::android::hardware::radio::V1_6::RadioError::NONE, radioRsp_v1_6->rspInfo.error);
+        ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_v1_6->rspInfo.error,
+            {::android::hardware::radio::V1_6::RadioError::NONE,
+             ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED},
+             CHECK_GENERAL_ERROR));
 
         if(pbCapacity.maxAdnRecords > 0
                 && pbCapacity.usedAdnRecords < pbCapacity.maxAdnRecords) {
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
index 4ff4574..58e02b3 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -831,14 +831,24 @@
     /**
      * DEVICE_UNIQUE_ATTESTATION is an argument to IKeyMintDevice::attested key generation/import
      * operations.  It indicates that attestation using a device-unique key is requested, rather
-     * than a batch key.  When a device-unique key is used, only the attestation certificate is
-     * returned; no additional chained certificates are provided.  It's up to the caller to
-     * recognize the device-unique signing key.  Only SecurityLevel::STRONGBOX IKeyMintDevices may
-     * support device-unique attestations.  SecurityLevel::TRUSTED_ENVIRONMENT IKeyMintDevices must
-     * return ErrorCode::INVALID_ARGUMENT if they receive DEVICE_UNIQUE_ATTESTATION.
+     * than a batch key.  When a device-unique key is used, the returned chain should contain two
+     * certificates:
+     *    * The attestation certificate, containing the attestation extension, as described in
+            KeyCreationResult.aidl.
+     *    * A self-signed root certificate, signed by the device-unique key.
+     * No additional chained certificates are provided. Only SecurityLevel::STRONGBOX
+     * IKeyMintDevices may support device-unique attestations.  SecurityLevel::TRUSTED_ENVIRONMENT
+     * IKeyMintDevices must return ErrorCode::INVALID_ARGUMENT if they receive
+     * DEVICE_UNIQUE_ATTESTATION.
      * SecurityLevel::STRONGBOX IKeyMintDevices need not support DEVICE_UNIQUE_ATTESTATION, and
      * return ErrorCode::CANNOT_ATTEST_IDS if they do not support it.
      *
+     * The caller needs to obtain the device-unique keys out-of-band and compare them against the
+     * key used to sign the self-signed root certificate.
+     * To ease this process, the IKeyMintDevice implementation should include, both in the subject
+     * and issuer fields of the self-signed root, the unique identifier of the device. Using the
+     * unique identifier will make it straightforward for the caller to link a device to its key.
+     *
      * IKeyMintDevice implementations that support device-unique attestation MUST add the
      * DEVICE_UNIQUE_ATTESTATION tag to device-unique attestations.
      */
diff --git a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
index b0f056a..732d9eb 100644
--- a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
+++ b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -42,8 +42,11 @@
 
         EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size)) << "Key size missing";
 
+        // The device-unique attestation chain should contain exactly two certificates:
+        // * The leaf with the attestation extension.
+        // * A self-signed root, signed using the device-unique key.
+        ASSERT_EQ(cert_chain_.size(), 2);
         EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
-        ASSERT_GT(cert_chain_.size(), 0);
 
         AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
         EXPECT_TRUE(verify_attestation_record("challenge", "foo", sw_enforced, hw_enforced,
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index c49b303..4d31fa4 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <functional>
 #include <string_view>
 
 #include <aidl/Gtest.h>
@@ -206,50 +207,58 @@
     template <typename TagType>
     std::tuple<KeyData /* aesKey */, KeyData /* hmacKey */, KeyData /* rsaKey */,
                KeyData /* ecdsaKey */>
-    CreateTestKeys(TagType tagToTest, ErrorCode expectedReturn) {
+    CreateTestKeys(
+            TagType tagToTest, ErrorCode expectedReturn,
+            std::function<void(AuthorizationSetBuilder*)> tagModifier =
+                    [](AuthorizationSetBuilder*) {}) {
         /* AES */
         KeyData aesKeyData;
-        ErrorCode errorCode = GenerateKey(AuthorizationSetBuilder()
-                                                  .AesEncryptionKey(128)
-                                                  .Authorization(tagToTest)
-                                                  .BlockMode(BlockMode::ECB)
-                                                  .Padding(PaddingMode::NONE)
-                                                  .Authorization(TAG_NO_AUTH_REQUIRED),
-                                          &aesKeyData.blob, &aesKeyData.characteristics);
+        AuthorizationSetBuilder aesBuilder = AuthorizationSetBuilder()
+                                                     .AesEncryptionKey(128)
+                                                     .Authorization(tagToTest)
+                                                     .BlockMode(BlockMode::ECB)
+                                                     .Padding(PaddingMode::NONE)
+                                                     .Authorization(TAG_NO_AUTH_REQUIRED);
+        tagModifier(&aesBuilder);
+        ErrorCode errorCode =
+                GenerateKey(aesBuilder, &aesKeyData.blob, &aesKeyData.characteristics);
         EXPECT_EQ(expectedReturn, errorCode);
 
         /* HMAC */
         KeyData hmacKeyData;
-        errorCode = GenerateKey(AuthorizationSetBuilder()
-                                        .HmacKey(128)
-                                        .Authorization(tagToTest)
-                                        .Digest(Digest::SHA_2_256)
-                                        .Authorization(TAG_MIN_MAC_LENGTH, 128)
-                                        .Authorization(TAG_NO_AUTH_REQUIRED),
-                                &hmacKeyData.blob, &hmacKeyData.characteristics);
+        AuthorizationSetBuilder hmacBuilder = AuthorizationSetBuilder()
+                                                      .HmacKey(128)
+                                                      .Authorization(tagToTest)
+                                                      .Digest(Digest::SHA_2_256)
+                                                      .Authorization(TAG_MIN_MAC_LENGTH, 128)
+                                                      .Authorization(TAG_NO_AUTH_REQUIRED);
+        tagModifier(&hmacBuilder);
+        errorCode = GenerateKey(hmacBuilder, &hmacKeyData.blob, &hmacKeyData.characteristics);
         EXPECT_EQ(expectedReturn, errorCode);
 
         /* RSA */
         KeyData rsaKeyData;
-        errorCode = GenerateKey(AuthorizationSetBuilder()
-                                        .RsaSigningKey(2048, 65537)
-                                        .Authorization(tagToTest)
-                                        .Digest(Digest::NONE)
-                                        .Padding(PaddingMode::NONE)
-                                        .Authorization(TAG_NO_AUTH_REQUIRED)
-                                        .SetDefaultValidity(),
-                                &rsaKeyData.blob, &rsaKeyData.characteristics);
+        AuthorizationSetBuilder rsaBuilder = AuthorizationSetBuilder()
+                                                     .RsaSigningKey(2048, 65537)
+                                                     .Authorization(tagToTest)
+                                                     .Digest(Digest::NONE)
+                                                     .Padding(PaddingMode::NONE)
+                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                     .SetDefaultValidity();
+        tagModifier(&rsaBuilder);
+        errorCode = GenerateKey(rsaBuilder, &rsaKeyData.blob, &rsaKeyData.characteristics);
         EXPECT_EQ(expectedReturn, errorCode);
 
         /* ECDSA */
         KeyData ecdsaKeyData;
-        errorCode = GenerateKey(AuthorizationSetBuilder()
-                                        .EcdsaSigningKey(256)
-                                        .Authorization(tagToTest)
-                                        .Digest(Digest::SHA_2_256)
-                                        .Authorization(TAG_NO_AUTH_REQUIRED)
-                                        .SetDefaultValidity(),
-                                &ecdsaKeyData.blob, &ecdsaKeyData.characteristics);
+        AuthorizationSetBuilder ecdsaBuilder = AuthorizationSetBuilder()
+                                                       .EcdsaSigningKey(256)
+                                                       .Authorization(tagToTest)
+                                                       .Digest(Digest::SHA_2_256)
+                                                       .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                       .SetDefaultValidity();
+        tagModifier(&ecdsaBuilder);
+        errorCode = GenerateKey(ecdsaBuilder, &ecdsaKeyData.blob, &ecdsaKeyData.characteristics);
         EXPECT_EQ(expectedReturn, errorCode);
         return {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData};
     }
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index 5dcfcaa..295be1a 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -6355,6 +6355,11 @@
     auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
             CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK);
 
+    for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) {
+        ASSERT_GT(keyData.blob.size(), 0U);
+        AuthorizationSet crypto_params = SecLevelAuthorizations(keyData.characteristics);
+        EXPECT_TRUE(crypto_params.Contains(TAG_EARLY_BOOT_ONLY)) << crypto_params;
+    }
     CheckedDeleteKey(&aesKeyData.blob);
     CheckedDeleteKey(&hmacKeyData.blob);
     CheckedDeleteKey(&rsaKeyData.blob);
@@ -6362,7 +6367,30 @@
 }
 
 /*
- * EarlyBootKeyTest.UsetEarlyBootKeyFailure
+ * EarlyBootKeyTest.CreateAttestedEarlyBootKey
+ *
+ * Verifies that creating an early boot key with attestation succeeds.
+ */
+TEST_P(EarlyBootKeyTest, CreateAttestedEarlyBootKey) {
+    auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] = CreateTestKeys(
+            TAG_EARLY_BOOT_ONLY, ErrorCode::OK, [](AuthorizationSetBuilder* builder) {
+                builder->AttestationChallenge("challenge");
+                builder->AttestationApplicationId("app_id");
+            });
+
+    for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) {
+        ASSERT_GT(keyData.blob.size(), 0U);
+        AuthorizationSet crypto_params = SecLevelAuthorizations(keyData.characteristics);
+        EXPECT_TRUE(crypto_params.Contains(TAG_EARLY_BOOT_ONLY)) << crypto_params;
+    }
+    CheckedDeleteKey(&aesKeyData.blob);
+    CheckedDeleteKey(&hmacKeyData.blob);
+    CheckedDeleteKey(&rsaKeyData.blob);
+    CheckedDeleteKey(&ecdsaKeyData.blob);
+}
+
+/*
+ * EarlyBootKeyTest.UseEarlyBootKeyFailure
  *
  * Verifies that using early boot keys at a later stage fails.
  */