Merge "VTS test for setAfcChannelAllowance" into main
diff --git a/audio/aidl/android/hardware/audio/core/IStreamOut.aidl b/audio/aidl/android/hardware/audio/core/IStreamOut.aidl
index f26dc1c..111969f 100644
--- a/audio/aidl/android/hardware/audio/core/IStreamOut.aidl
+++ b/audio/aidl/android/hardware/audio/core/IStreamOut.aidl
@@ -214,7 +214,8 @@
      *
      * The range of supported values for speed and pitch factors is provided by
      * the 'IModule.getSupportedPlaybackRateFactors' method. Out of range speed
-     * and pitch values must not be rejected if the fallback mode is 'MUTE'.
+     * and pitch values may result in silent playback instead of returning an
+     * error in the case when the fallback mode is 'MUTE'.
      *
      * @param playbackRate Playback parameters to set.
      * @throws EX_ILLEGAL_ARGUMENT If provided parameters are out of acceptable range.
diff --git a/audio/aidl/common/tests/utils_tests.cpp b/audio/aidl/common/tests/utils_tests.cpp
index 1b8b8df..1522d7e 100644
--- a/audio/aidl/common/tests/utils_tests.cpp
+++ b/audio/aidl/common/tests/utils_tests.cpp
@@ -86,7 +86,7 @@
             std::make_pair(6UL, AudioChannelLayout::LAYOUT_5POINT1),
             std::make_pair(8UL, AudioChannelLayout::LAYOUT_7POINT1),
             std::make_pair(16UL, AudioChannelLayout::LAYOUT_9POINT1POINT6),
-            std::make_pair(13UL, AudioChannelLayout::LAYOUT_13POINT_360RA),
+            std::make_pair(13UL, AudioChannelLayout::LAYOUT_13POINT0),
             std::make_pair(24UL, AudioChannelLayout::LAYOUT_22POINT2),
             std::make_pair(3UL, AudioChannelLayout::LAYOUT_STEREO_HAPTIC_A),
             std::make_pair(4UL, AudioChannelLayout::LAYOUT_STEREO_HAPTIC_AB)};
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 967d0b9..687d8fc 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -121,6 +121,52 @@
     ],
 }
 
+cc_library {
+    name: "libeffectconfig",
+    srcs: [
+        "EffectConfig.cpp",
+    ],
+    defaults: [
+        "latest_android_hardware_audio_effect_ndk_shared",
+        "latest_android_media_audio_common_types_ndk_shared",
+    ],
+    shared_libs: [
+        "libaudioutils",
+        "libaudio_aidl_conversion_common_ndk",
+        "libbase",
+        "libbinder_ndk",
+        "liblog",
+        "libmedia_helper",
+        "libtinyxml2",
+        "libutils",
+    ],
+    header_libs: [
+        "libaudio_system_headers",
+        "libaudioaidl_headers",
+    ],
+    export_shared_lib_headers: [
+        "libtinyxml2",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-DBACKEND_NDK",
+    ],
+    vendor: true,
+    host_supported: true,
+    target: {
+        android: {
+            shared_libs: [
+                "libapexsupport",
+            ],
+        },
+    },
+}
+
 cc_binary {
     name: "android.hardware.audio.service-aidl.example",
     relative_install_path: "hw",
@@ -290,10 +336,9 @@
     defaults: ["aidlaudioeffectservice_defaults"],
     shared_libs: [
         "libapexsupport",
-        "libtinyxml2",
+        "libeffectconfig",
     ],
     srcs: [
-        "EffectConfig.cpp",
         "EffectFactory.cpp",
         "EffectMain.cpp",
     ],
diff --git a/audio/aidl/default/EffectConfig.cpp b/audio/aidl/default/EffectConfig.cpp
index 9c335ba..fa12056 100644
--- a/audio/aidl/default/EffectConfig.cpp
+++ b/audio/aidl/default/EffectConfig.cpp
@@ -106,6 +106,7 @@
 }
 
 bool EffectConfig::resolveLibrary(const std::string& path, std::string* resolvedPath) {
+#ifdef __ANDROID_APEX__
     if constexpr (__ANDROID_VENDOR_API__ >= 202404) {
         AApexInfo *apexInfo;
         if (AApexInfo_create(&apexInfo) == AAPEXINFO_OK) {
@@ -122,6 +123,7 @@
     } else {
         LOG(DEBUG) << __func__ << " libapexsupport is not supported";
     }
+#endif
 
     // If audio effects libs are not in vendor apex, locate them in kEffectLibPath
     for (auto* libraryDirectory : kEffectLibPath) {
diff --git a/audio/aidl/default/EngineConfigXmlConverter.cpp b/audio/aidl/default/EngineConfigXmlConverter.cpp
index 5a8b0a3..d945b17 100644
--- a/audio/aidl/default/EngineConfigXmlConverter.cpp
+++ b/audio/aidl/default/EngineConfigXmlConverter.cpp
@@ -179,8 +179,13 @@
         const eng_xsd::ProductStrategies::ProductStrategy& xsdcProductStrategy) {
     AudioHalProductStrategy aidlProductStrategy;
 
-    aidlProductStrategy.id =
-            VALUE_OR_FATAL(convertProductStrategyIdToAidl(xsdcProductStrategy.getId()));
+    if (xsdcProductStrategy.hasId()) {
+        aidlProductStrategy.id =
+                VALUE_OR_FATAL(convertProductStrategyIdToAidl(xsdcProductStrategy.getId()));
+    } else {
+        aidlProductStrategy.id =
+                VALUE_OR_FATAL(convertProductStrategyNameToAidl(xsdcProductStrategy.getName()));
+    }
     aidlProductStrategy.name = xsdcProductStrategy.getName();
 
     if (xsdcProductStrategy.hasAttributesGroup()) {
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index e96cf81..f9fa799 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -184,8 +184,12 @@
     const int32_t nominalLatencyMs = getNominalLatencyMs(*portConfigIt);
     // Since this is a private method, it is assumed that
     // validity of the portConfigId has already been checked.
-    const int32_t minimumStreamBufferSizeFrames =
-            calculateBufferSizeFrames(nominalLatencyMs, portConfigIt->sampleRate.value().value);
+    int32_t minimumStreamBufferSizeFrames = 0;
+    if (!calculateBufferSizeFrames(
+                portConfigIt->format.value(), nominalLatencyMs,
+                portConfigIt->sampleRate.value().value, &minimumStreamBufferSizeFrames).isOk()) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
     if (in_bufferSizeFrames < minimumStreamBufferSizeFrames) {
         LOG(ERROR) << __func__ << ": " << mType << ": insufficient buffer size "
                    << in_bufferSizeFrames << ", must be at least " << minimumStreamBufferSizeFrames;
@@ -378,6 +382,18 @@
     return kLatencyMs;
 }
 
+ndk::ScopedAStatus Module::calculateBufferSizeFrames(
+        const ::aidl::android::media::audio::common::AudioFormatDescription &format,
+        int32_t latencyMs, int32_t sampleRateHz, int32_t *bufferSizeFrames) {
+    if (format.type == AudioFormatType::PCM) {
+        *bufferSizeFrames = calculateBufferSizeFramesForPcm(latencyMs, sampleRateHz);
+        return ndk::ScopedAStatus::ok();
+    }
+    LOG(ERROR) << __func__ << ": " << mType << ": format " << format.toString()
+        << " is not supported";
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
 ndk::ScopedAStatus Module::createMmapBuffer(
         const ::aidl::android::hardware::audio::core::StreamContext& context __unused,
         ::aidl::android::hardware::audio::core::StreamDescriptor* desc __unused) {
@@ -1123,8 +1139,14 @@
     *_aidl_return = in_requested;
     auto maxSampleRateIt = std::max_element(sampleRates.begin(), sampleRates.end());
     const int32_t latencyMs = getNominalLatencyMs(*(maxSampleRateIt->second));
-    _aidl_return->minimumStreamBufferSizeFrames =
-            calculateBufferSizeFrames(latencyMs, maxSampleRateIt->first);
+    if (!calculateBufferSizeFrames(
+                maxSampleRateIt->second->format.value(), latencyMs, maxSampleRateIt->first,
+                &_aidl_return->minimumStreamBufferSizeFrames).isOk()) {
+        if (patchesBackup.has_value()) {
+            mPatches = std::move(*patchesBackup);
+        }
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
     _aidl_return->latenciesMs.clear();
     _aidl_return->latenciesMs.insert(_aidl_return->latenciesMs.end(),
                                      _aidl_return->sinkPortConfigIds.size(), latencyMs);
diff --git a/audio/aidl/default/config/audioPolicy/api/current.txt b/audio/aidl/default/config/audioPolicy/api/current.txt
index 1249a09..c675820 100644
--- a/audio/aidl/default/config/audioPolicy/api/current.txt
+++ b/audio/aidl/default/config/audioPolicy/api/current.txt
@@ -50,7 +50,7 @@
     enum_constant public static final android.audio.policy.configuration.AudioChannelMask AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO;
     enum_constant public static final android.audio.policy.configuration.AudioChannelMask AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO;
     enum_constant public static final android.audio.policy.configuration.AudioChannelMask AUDIO_CHANNEL_NONE;
-    enum_constant public static final android.audio.policy.configuration.AudioChannelMask AUDIO_CHANNEL_OUT_13POINT_360RA;
+    enum_constant public static final android.audio.policy.configuration.AudioChannelMask AUDIO_CHANNEL_OUT_13POINT0;
     enum_constant public static final android.audio.policy.configuration.AudioChannelMask AUDIO_CHANNEL_OUT_22POINT2;
     enum_constant public static final android.audio.policy.configuration.AudioChannelMask AUDIO_CHANNEL_OUT_2POINT0POINT2;
     enum_constant public static final android.audio.policy.configuration.AudioChannelMask AUDIO_CHANNEL_OUT_2POINT1;
diff --git a/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd b/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd
index 8adac8c..94856a5 100644
--- a/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd
+++ b/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd
@@ -476,7 +476,7 @@
             <xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1POINT4"/>
             <xs:enumeration value="AUDIO_CHANNEL_OUT_9POINT1POINT4"/>
             <xs:enumeration value="AUDIO_CHANNEL_OUT_9POINT1POINT6"/>
-            <xs:enumeration value="AUDIO_CHANNEL_OUT_13POINT_360RA"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_13POINT0"/>
             <xs:enumeration value="AUDIO_CHANNEL_OUT_22POINT2"/>
             <xs:enumeration value="AUDIO_CHANNEL_OUT_MONO_HAPTIC_A"/>
             <xs:enumeration value="AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A"/>
diff --git a/audio/aidl/default/config/audioPolicy/engine/audio_policy_engine_configuration.xsd b/audio/aidl/default/config/audioPolicy/engine/audio_policy_engine_configuration.xsd
index 7d9b06e..02250c7 100644
--- a/audio/aidl/default/config/audioPolicy/engine/audio_policy_engine_configuration.xsd
+++ b/audio/aidl/default/config/audioPolicy/engine/audio_policy_engine_configuration.xsd
@@ -105,7 +105,8 @@
                         <xs:element name="AttributesGroup" type="AttributesGroup" minOccurs="1" maxOccurs="unbounded"/>
                     </xs:sequence>
                     <xs:attribute name="name" type="xs:string" use="required"/>
-                    <xs:attribute name="id" type="xs:int" use="required"/>
+                    <!-- Only needs to be specified for vendor strategies. -->
+                    <xs:attribute name="id" type="xs:int" use="optional"/>
                 </xs:complexType>
             </xs:element>
         </xs:sequence>
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index d03598a..cbc13d1 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -207,12 +207,15 @@
     virtual std::unique_ptr<Configuration> initializeConfig();
     virtual int32_t getNominalLatencyMs(
             const ::aidl::android::media::audio::common::AudioPortConfig& portConfig);
+    virtual ndk::ScopedAStatus calculateBufferSizeFrames(
+            const ::aidl::android::media::audio::common::AudioFormatDescription &format,
+            int32_t latencyMs, int32_t sampleRateHz, int32_t *bufferSizeFrames);
     virtual ndk::ScopedAStatus createMmapBuffer(
             const ::aidl::android::hardware::audio::core::StreamContext& context,
             ::aidl::android::hardware::audio::core::StreamDescriptor* desc);
 
     // Utility and helper functions accessible to subclasses.
-    static int32_t calculateBufferSizeFrames(int32_t latencyMs, int32_t sampleRateHz) {
+    static int32_t calculateBufferSizeFramesForPcm(int32_t latencyMs, int32_t sampleRateHz) {
         const int32_t rawSizeFrames =
                 aidl::android::hardware::audio::common::frameCountFromDurationMs(latencyMs,
                                                                                  sampleRateHz);
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index ea59771..cef0ea6 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -77,17 +77,15 @@
 }
 
 ::android::status_t StreamRemoteSubmix::drain(StreamDescriptor::DrainMode) {
-    usleep(1000);
     return ::android::OK;
 }
 
 ::android::status_t StreamRemoteSubmix::flush() {
-    usleep(1000);
-    return ::android::OK;
+    // TODO(b/372951987): consider if this needs to be done from 'StreamInWorkerLogic::cycle'.
+    return mIsInput ? standby() : ::android::OK;
 }
 
 ::android::status_t StreamRemoteSubmix::pause() {
-    usleep(1000);
     return ::android::OK;
 }
 
diff --git a/audio/aidl/default/r_submix/SubmixRoute.cpp b/audio/aidl/default/r_submix/SubmixRoute.cpp
index 325a012..445b1d3 100644
--- a/audio/aidl/default/r_submix/SubmixRoute.cpp
+++ b/audio/aidl/default/r_submix/SubmixRoute.cpp
@@ -134,10 +134,10 @@
 // - the peer input is in standby AFTER having been active.
 // We DO block if:
 // - the input was never activated to avoid discarding first frames in the pipe in case capture
-// start was delayed
+//   start was delayed
 bool SubmixRoute::shouldBlockWrite() {
     std::lock_guard guard(mLock);
-    return (mStreamInOpen || (mStreamInStandby && (mReadCounterFrames != 0)));
+    return mStreamInOpen && (!mStreamInStandby || mReadCounterFrames == 0);
 }
 
 long SubmixRoute::updateReadCounterFrames(size_t frameCount) {
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index 6bce107..750e54d 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -376,7 +376,8 @@
 template <typename PropType, class Instance, typename Getter, typename Setter>
 void TestAccessors(Instance* inst, Getter getter, Setter setter,
                    const std::vector<PropType>& validValues,
-                   const std::vector<PropType>& invalidValues, bool* isSupported) {
+                   const std::vector<PropType>& invalidValues, bool* isSupported,
+                   const std::vector<PropType>* ambivalentValues = nullptr) {
     PropType initialValue{};
     ScopedAStatus status = (inst->*getter)(&initialValue);
     if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
@@ -395,6 +396,15 @@
         EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, (inst->*setter)(v))
                 << "for an invalid value: " << ::testing::PrintToString(v);
     }
+    if (ambivalentValues != nullptr) {
+        for (const auto v : *ambivalentValues) {
+            const auto status = (inst->*setter)(v);
+            if (!status.isOk()) {
+                EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, status)
+                        << "for an ambivalent value: " << ::testing::PrintToString(v);
+            }
+        }
+    }
     EXPECT_IS_OK((inst->*setter)(initialValue)) << "Failed to restore the initial value";
 }
 
@@ -928,6 +938,8 @@
         LOG(DEBUG) << __func__;
         return "";
     }
+    const std::vector<int8_t>& getData() const { return mData; }
+    void fillData(int8_t filler) { std::fill(mData.begin(), mData.end(), filler); }
     std::optional<StreamDescriptor::Command> maybeGetNextCommand(int* actualSize = nullptr) {
         TransitionTrigger trigger = mDriver->getNextTrigger(mData.size(), actualSize);
         if (StreamEventReceiver::Event* expEvent =
@@ -997,6 +1009,8 @@
     StreamReaderLogic(const StreamContext& context, StreamLogicDriver* driver,
                       StreamEventReceiver* eventReceiver)
         : StreamCommonLogic(context, driver, eventReceiver) {}
+    // Should only be called after the worker has joined.
+    const std::vector<int8_t>& getData() const { return StreamCommonLogic::getData(); }
 
   protected:
     Status cycle() override {
@@ -1062,6 +1076,7 @@
         if (const size_t readCount =
                     !isMmapped() ? getDataMQ()->availableToRead() : reply.fmqByteCount;
             readCount > 0) {
+            fillData(-1);
             if (isMmapped() ? readDataFromMmap(readCount) : readDataFromMQ(readCount)) {
                 goto checkAcceptedReply;
             }
@@ -1083,6 +1098,8 @@
     StreamWriterLogic(const StreamContext& context, StreamLogicDriver* driver,
                       StreamEventReceiver* eventReceiver)
         : StreamCommonLogic(context, driver, eventReceiver) {}
+    // Should only be called after the worker has joined.
+    const std::vector<int8_t>& getData() const { return StreamCommonLogic::getData(); }
 
   protected:
     Status cycle() override {
@@ -1099,6 +1116,14 @@
             return Status::ABORT;
         }
         if (actualSize != 0) {
+            if (command.getTag() == StreamDescriptor::Command::burst) {
+                fillData(mBurstIteration);
+                if (mBurstIteration < std::numeric_limits<int8_t>::max()) {
+                    mBurstIteration++;
+                } else {
+                    mBurstIteration = 0;
+                }
+            }
             if (isMmapped() ? !writeDataToMmap() : !writeDataToMQ()) {
                 return Status::ABORT;
             }
@@ -1157,6 +1182,9 @@
         LOG(ERROR) << __func__ << ": unacceptable reply: " << reply.toString();
         return Status::ABORT;
     }
+
+  private:
+    int8_t mBurstIteration = 1;
 };
 using StreamWriter = StreamWorker<StreamWriterLogic>;
 
@@ -2849,10 +2877,12 @@
         ASSERT_NO_FATAL_FAILURE(mStream->SetUpStream(module, getMinimumStreamBufferSizeFrames()));
     }
 
-    void SetUpStreamForDevicePort(IModule* module, ModuleConfig* moduleConfig,
-                                  const AudioPort& devicePort, bool connectedOnly = false) {
-        ASSERT_NO_FATAL_FAILURE(
-                SetUpPortConfigForDevicePort(module, moduleConfig, devicePort, connectedOnly));
+    void SetUpStreamForDevicePort(
+            IModule* module, ModuleConfig* moduleConfig, const AudioPort& devicePort,
+            bool connectedOnly = false,
+            const std::optional<AudioDeviceAddress>& connectionAddress = std::nullopt) {
+        ASSERT_NO_FATAL_FAILURE(SetUpPortConfigForDevicePort(module, moduleConfig, devicePort,
+                                                             connectedOnly, connectionAddress));
         if (!mSkipTestReason.empty()) return;
         ASSERT_NO_FATAL_FAILURE(SetUpStream(module));
     }
@@ -2888,6 +2918,23 @@
         if (!mSkipTestReason.empty()) return;
         ASSERT_NO_FATAL_FAILURE(SetUpStream(module));
     }
+    void SetUpStreamForNewMixPortConfig(IModule* module, ModuleConfig*,
+                                        const AudioPortConfig& existingMixPortConfig,
+                                        const AudioPortConfig& existingDevicePortConfig) {
+        auto mixPortConfig = existingMixPortConfig;
+        mixPortConfig.id = 0;
+        mMixPortConfig = std::make_unique<WithAudioPortConfig>(mixPortConfig);
+        ASSERT_NO_FATAL_FAILURE(mMixPortConfig->SetUp(module));
+        mDevicePortConfig = std::make_unique<WithAudioPortConfig>(existingDevicePortConfig);
+        ASSERT_NO_FATAL_FAILURE(mDevicePortConfig->SetUp(module));
+        mDevice = existingDevicePortConfig.ext.get<AudioPortExt::device>().device;
+        mPatch = std::make_unique<WithAudioPatch>(mIsInput, mMixPortConfig->get(),
+                                                  mDevicePortConfig->get());
+        ASSERT_NO_FATAL_FAILURE(mPatch->SetUp(module));
+        mStream = std::make_unique<WithStream<Stream>>(mMixPortConfig->get());
+        ASSERT_NO_FATAL_FAILURE(mStream->SetUpPortConfig(module));
+        ASSERT_NO_FATAL_FAILURE(SetUpStream(module));
+    }
     void SetUpPatchForMixPortConfig(IModule* module, ModuleConfig* moduleConfig,
                                     const AudioPortConfig& mixPortConfig) {
         constexpr bool connectedOnly = true;
@@ -2919,6 +2966,7 @@
     }
 
     const AudioDevice& getDevice() const { return mDevice; }
+    const AudioPortConfig& getDevicePortConfig() const { return mDevicePortConfig->get(); }
     int32_t getMinimumStreamBufferSizeFrames() const {
         return mPatch->getMinimumStreamBufferSizeFrames();
     }
@@ -2934,7 +2982,8 @@
   private:
     void SetUpDevicePort(IModule* module, ModuleConfig* moduleConfig,
                          const std::set<int32_t>& devicePortIds, bool connectedOnly,
-                         std::optional<AudioPort>* connectedDevicePort) {
+                         std::optional<AudioPort>* connectedDevicePort,
+                         const std::optional<AudioDeviceAddress>& connectionAddress) {
         const auto attachedDevicePorts = moduleConfig->getAttachedDevicePorts();
         if (auto it = findAny<AudioPort>(attachedDevicePorts, devicePortIds);
             it != attachedDevicePorts.end()) {
@@ -2951,7 +3000,12 @@
             const auto externalDevicePorts = moduleConfig->getExternalDevicePorts();
             if (auto it = findAny<AudioPort>(externalDevicePorts, devicePortIds);
                 it != externalDevicePorts.end()) {
-                AudioPort portWithData = GenerateUniqueDeviceAddress(*it);
+                AudioPort portWithData = *it;
+                if (connectionAddress.has_value()) {
+                    portWithData.ext.get<AudioPortExt::Tag::device>().device.address =
+                            *connectionAddress;
+                }
+                portWithData = GenerateUniqueDeviceAddress(portWithData);
                 mPortConnected = std::make_unique<WithDevicePortConnectedState>(portWithData);
                 ASSERT_NO_FATAL_FAILURE(mPortConnected->SetUp(module, moduleConfig));
                 *connectedDevicePort = mPortConnected->get();
@@ -2970,9 +3024,9 @@
             LOG(DEBUG) << __func__ << ": " << mSkipTestReason;
             return;
         };
-        ASSERT_NO_FATAL_FAILURE(SetUpDevicePort(module, moduleConfig,
-                                                extractIds<AudioPort>(devicePorts), connectedOnly,
-                                                connectedDevicePort));
+        ASSERT_NO_FATAL_FAILURE(SetUpDevicePort(
+                module, moduleConfig, extractIds<AudioPort>(devicePorts), connectedOnly,
+                connectedDevicePort, std::nullopt /*connectionAddress*/));
         if (!connectedDevicePort->has_value()) {
             mSkipTestReason = std::string("Unable to find a device port pair for mix port id ")
                                       .append(std::to_string(mixPort.id));
@@ -2980,11 +3034,14 @@
             return;
         }
     }
-    void SetUpPortConfigForDevicePort(IModule* module, ModuleConfig* moduleConfig,
-                                      const AudioPort& devicePort, bool connectedOnly) {
+    void SetUpPortConfigForDevicePort(
+            IModule* module, ModuleConfig* moduleConfig, const AudioPort& devicePort,
+            bool connectedOnly,
+            const std::optional<AudioDeviceAddress>& connectionAddress = std::nullopt) {
         std::optional<AudioPort> connectedDevicePort;
         ASSERT_NO_FATAL_FAILURE(SetUpDevicePort(module, moduleConfig, {devicePort.id},
-                                                connectedOnly, &connectedDevicePort));
+                                                connectedOnly, &connectedDevicePort,
+                                                connectionAddress));
         if (!connectedDevicePort.has_value()) {
             mSkipTestReason = std::string("Device port id ")
                                       .append(std::to_string(devicePort.id))
@@ -3125,7 +3182,8 @@
 };
 
 // Defined later together with state transition sequences.
-std::shared_ptr<StateSequence> makeBurstCommands(bool isSync);
+std::shared_ptr<StateSequence> makeBurstCommands(bool isSync, size_t burstCount = 10,
+                                                 bool standbyInputWhenDone = false);
 
 // Certain types of ports can not be used without special preconditions.
 static bool skipStreamIoTestForMixPortConfig(const AudioPortConfig& portConfig) {
@@ -3150,10 +3208,11 @@
   public:
     explicit StreamFixtureWithWorker(bool isSync) : mIsSync(isSync) {}
 
-    void SetUp(IModule* module, ModuleConfig* moduleConfig, const AudioPort& devicePort) {
+    void SetUp(IModule* module, ModuleConfig* moduleConfig, const AudioPort& devicePort,
+               const std::optional<AudioDeviceAddress>& connectionAddress = std::nullopt) {
         mStream = std::make_unique<StreamFixture<Stream>>();
-        ASSERT_NO_FATAL_FAILURE(
-                mStream->SetUpStreamForDevicePort(module, moduleConfig, devicePort));
+        ASSERT_NO_FATAL_FAILURE(mStream->SetUpStreamForDevicePort(
+                module, moduleConfig, devicePort, false /*connectedOnly*/, connectionAddress));
         MaybeSetSkipTestReason();
     }
 
@@ -3165,26 +3224,42 @@
         MaybeSetSkipTestReason();
     }
 
-    void SendBurstCommands(bool validatePosition = true) {
-        ASSERT_NO_FATAL_FAILURE(StartWorkerToSendBurstCommands());
+    void SetUp(IModule* module, ModuleConfig* moduleConfig,
+               const AudioPortConfig& existingMixPortConfig,
+               const AudioPortConfig& existingDevicePortConfig) {
+        mStream = std::make_unique<StreamFixture<Stream>>();
+        ASSERT_NO_FATAL_FAILURE(mStream->SetUpStreamForNewMixPortConfig(
+                module, moduleConfig, existingMixPortConfig, existingDevicePortConfig));
+        MaybeSetSkipTestReason();
+    }
+
+    void SendBurstCommands(bool validatePosition = true, size_t burstCount = 10,
+                           bool standbyInputWhenDone = false) {
+        ASSERT_NO_FATAL_FAILURE(StartWorkerToSendBurstCommands(burstCount, standbyInputWhenDone));
         ASSERT_NO_FATAL_FAILURE(JoinWorkerAfterBurstCommands(validatePosition));
     }
 
-    void StartWorkerToSendBurstCommands() {
+    void StartWorkerToSendBurstCommands(size_t burstCount = 10, bool standbyInputWhenDone = false) {
+        if (!IOTraits<Stream>::is_input) {
+            ASSERT_FALSE(standbyInputWhenDone) << "Only supported for input";
+        }
         const StreamContext* context = mStream->getStreamContext();
         mWorkerDriver = std::make_unique<StreamLogicDefaultDriver>(
-                makeBurstCommands(mIsSync), context->getFrameSizeBytes(), context->isMmapped());
+                makeBurstCommands(mIsSync, burstCount, standbyInputWhenDone),
+                context->getFrameSizeBytes(), context->isMmapped());
         mWorker = std::make_unique<typename IOTraits<Stream>::Worker>(
                 *context, mWorkerDriver.get(), mStream->getStreamEventReceiver());
         LOG(DEBUG) << __func__ << ": starting " << IOTraits<Stream>::directionStr << " worker...";
         ASSERT_TRUE(mWorker->start());
     }
 
-    void JoinWorkerAfterBurstCommands(bool validatePosition = true) {
-        // Must call 'prepareToClose' before attempting to join because the stream may be stuck.
-        std::shared_ptr<IStreamCommon> common;
-        ASSERT_IS_OK(mStream->getStream()->getStreamCommon(&common));
-        ASSERT_IS_OK(common->prepareToClose());
+    void JoinWorkerAfterBurstCommands(bool validatePosition = true,
+                                      bool callPrepareToClose = true) {
+        if (callPrepareToClose) {
+            std::shared_ptr<IStreamCommon> common;
+            ASSERT_IS_OK(mStream->getStream()->getStreamCommon(&common));
+            ASSERT_IS_OK(common->prepareToClose());
+        }
         LOG(DEBUG) << __func__ << ": joining " << IOTraits<Stream>::directionStr << " worker...";
         mWorker->join();
         EXPECT_FALSE(mWorker->hasError()) << mWorker->getError();
@@ -3195,6 +3270,7 @@
             EXPECT_FALSE(mWorkerDriver->hasObservableRetrogradePosition());
             EXPECT_FALSE(mWorkerDriver->hasHardwareRetrogradePosition());
         }
+        mLastData = mWorker->getData();
         mWorker.reset();
         mWorkerDriver.reset();
     }
@@ -3202,6 +3278,9 @@
     void TeardownPatch() { mStream->TeardownPatch(); }
 
     const AudioDevice& getDevice() const { return mStream->getDevice(); }
+    const AudioPortConfig& getDevicePortConfig() const { return mStream->getDevicePortConfig(); }
+    const std::vector<int8_t>& getLastData() const { return mLastData; }
+    const AudioPortConfig& getPortConfig() const { return mStream->getPortConfig(); }
     Stream* getStream() const { return mStream->getStream(); }
     std::string skipTestReason() const {
         return !mSkipTestReason.empty() ? mSkipTestReason : mStream->skipTestReason();
@@ -3219,6 +3298,7 @@
     std::unique_ptr<StreamFixture<Stream>> mStream;
     std::unique_ptr<StreamLogicDefaultDriver> mWorkerDriver;
     std::unique_ptr<typename IOTraits<Stream>::Worker> mWorker;
+    std::vector<int8_t> mLastData;
 };
 
 template <typename Stream>
@@ -3936,11 +4016,6 @@
             AudioPlaybackRate{1.0f, 1.0f, tsVoice, fbFail},
             AudioPlaybackRate{factors.maxSpeed, factors.maxPitch, tsVoice, fbMute},
             AudioPlaybackRate{factors.minSpeed, factors.minPitch, tsVoice, fbMute},
-            // Out of range speed / pitch values must not be rejected if the fallback mode is "mute"
-            AudioPlaybackRate{factors.maxSpeed * 2, factors.maxPitch * 2, tsDefault, fbMute},
-            AudioPlaybackRate{factors.minSpeed / 2, factors.minPitch / 2, tsDefault, fbMute},
-            AudioPlaybackRate{factors.maxSpeed * 2, factors.maxPitch * 2, tsVoice, fbMute},
-            AudioPlaybackRate{factors.minSpeed / 2, factors.minPitch / 2, tsVoice, fbMute},
     };
     const std::vector<AudioPlaybackRate> invalidValues = {
             AudioPlaybackRate{factors.maxSpeed, factors.maxPitch * 2, tsDefault, fbFail},
@@ -3956,6 +4031,14 @@
             AudioPlaybackRate{1.0f, 1.0f, tsDefault,
                               AudioPlaybackRate::TimestretchFallbackMode::SYS_RESERVED_DEFAULT},
     };
+    const std::vector<AudioPlaybackRate> ambivalentValues = {
+            // Out of range speed / pitch values may optionally be rejected if the fallback mode
+            // is "mute".
+            AudioPlaybackRate{factors.maxSpeed * 2, factors.maxPitch * 2, tsDefault, fbMute},
+            AudioPlaybackRate{factors.minSpeed / 2, factors.minPitch / 2, tsDefault, fbMute},
+            AudioPlaybackRate{factors.maxSpeed * 2, factors.maxPitch * 2, tsVoice, fbMute},
+            AudioPlaybackRate{factors.minSpeed / 2, factors.minPitch / 2, tsVoice, fbMute},
+    };
     bool atLeastOneSupports = false;
     for (const auto& port : offloadMixPorts) {
         const auto portConfig = moduleConfig->getSingleConfigForMixPort(false, port);
@@ -3965,7 +4048,8 @@
         bool isSupported = false;
         EXPECT_NO_FATAL_FAILURE(TestAccessors<AudioPlaybackRate>(
                 stream.get(), &IStreamOut::getPlaybackRateParameters,
-                &IStreamOut::setPlaybackRateParameters, validValues, invalidValues, &isSupported));
+                &IStreamOut::setPlaybackRateParameters, validValues, invalidValues, &isSupported,
+                &ambivalentValues));
         if (isSupported) atLeastOneSupports = true;
     }
     if (!atLeastOneSupports) {
@@ -4562,15 +4646,20 @@
 
 // TODO: Add async test cases for input once it is implemented.
 
-std::shared_ptr<StateSequence> makeBurstCommands(bool isSync) {
+std::shared_ptr<StateSequence> makeBurstCommands(bool isSync, size_t burstCount,
+                                                 bool standbyInputWhenDone) {
     using State = StreamDescriptor::State;
     auto d = std::make_unique<StateDag>();
-    StateDag::Node last = d->makeFinalNode(State::ACTIVE);
+    StateDag::Node active = d->makeFinalNode(State::ACTIVE);
+    StateDag::Node paused = d->makeNodes({std::make_pair(State::ACTIVE, kPauseCommand),
+                                          std::make_pair(State::PAUSED, kFlushCommand)},
+                                         State::STANDBY);
+    StateDag::Node& last = standbyInputWhenDone ? paused : active;
     if (isSync) {
         StateDag::Node idle = d->makeNode(
                 State::IDLE, kBurstCommand,
                 // Use several bursts to ensure that the driver starts reporting the position.
-                d->makeNodes(State::ACTIVE, kBurstCommand, 10, last));
+                d->makeNodes(State::ACTIVE, kBurstCommand, burstCount, last));
         d->makeNode(State::STANDBY, kStartCommand, idle);
     } else {
         StateDag::Node active2 = d->makeNode(State::ACTIVE, kBurstCommand, last);
@@ -4935,49 +5024,69 @@
   public:
     WithRemoteSubmix() : mStream(true /*isSync*/) {}
     explicit WithRemoteSubmix(AudioDeviceAddress address)
-        : mStream(true /*isSync*/), mAddress(address) {}
+        : mStream(true /*isSync*/), mAddress(address) {
+        LOG(DEBUG) << __func__ << ": Creating " << IOTraits<Stream>::directionStr
+                   << " stream for: " << mAddress.value_or(AudioDeviceAddress{}).toString();
+    }
     WithRemoteSubmix(const WithRemoteSubmix&) = delete;
     WithRemoteSubmix& operator=(const WithRemoteSubmix&) = delete;
+    ~WithRemoteSubmix() {
+        LOG(DEBUG) << __func__ << ": Deleting " << IOTraits<Stream>::directionStr
+                   << " stream for: " << mAddress.value_or(AudioDeviceAddress{}).toString();
+    }
 
-    static std::optional<AudioPort> getRemoteSubmixAudioPort(
-            ModuleConfig* moduleConfig,
-            const std::optional<AudioDeviceAddress>& address = std::nullopt) {
+    static std::optional<AudioPort> getRemoteSubmixAudioPort(ModuleConfig* moduleConfig) {
         auto ports =
                 moduleConfig->getRemoteSubmixPorts(IOTraits<Stream>::is_input, true /*singlePort*/);
         if (ports.empty()) return {};
-        AudioPort port = ports.front();
-        if (address) {
-            port.ext.template get<AudioPortExt::Tag::device>().device.address = address.value();
-        }
-        return port;
+        return ports.front();
     }
 
     void SetUp(IModule* module, ModuleConfig* moduleConfig) {
-        auto devicePort = getRemoteSubmixAudioPort(moduleConfig, mAddress);
+        auto devicePort = getRemoteSubmixAudioPort(moduleConfig);
         ASSERT_TRUE(devicePort.has_value()) << "Device port for remote submix device not found";
-        ASSERT_NO_FATAL_FAILURE(mStream.SetUp(module, moduleConfig, *devicePort));
+        ASSERT_NO_FATAL_FAILURE(mStream.SetUp(module, moduleConfig, *devicePort, mAddress));
         mAddress = mStream.getDevice().address;
     }
-
-    void StartWorkerToSendBurstCommands() {
-        ASSERT_NO_FATAL_FAILURE(mStream.StartWorkerToSendBurstCommands());
+    void SetUp(IModule* module, ModuleConfig* moduleConfig,
+               const AudioPortConfig& existingMixPortConfig,
+               const AudioPortConfig& existingDevicePortConfig) {
+        ASSERT_NO_FATAL_FAILURE(mStream.SetUp(module, moduleConfig, existingMixPortConfig,
+                                              existingDevicePortConfig));
+        mAddress = mStream.getDevice().address;
+    }
+    void StartWorkerToSendBurstCommands(size_t burstCount = 10, bool standbyInputWhenDone = false) {
+        ASSERT_NO_FATAL_FAILURE(
+                mStream.StartWorkerToSendBurstCommands(burstCount, standbyInputWhenDone));
     }
 
-    void JoinWorkerAfterBurstCommands() {
-        ASSERT_NO_FATAL_FAILURE(mStream.JoinWorkerAfterBurstCommands());
+    void JoinWorkerAfterBurstCommands(bool callPrepareToCloseBeforeJoin) {
+        ASSERT_NO_FATAL_FAILURE(mStream.JoinWorkerAfterBurstCommands(
+                true /*validatePositionIncrease*/, callPrepareToCloseBeforeJoin));
     }
 
-    void SendBurstCommands() {
-        ASSERT_NO_FATAL_FAILURE(mStream.StartWorkerToSendBurstCommands());
-        ASSERT_NO_FATAL_FAILURE(mStream.JoinWorkerAfterBurstCommands());
+    void JoinWorkerAfterBurstCommands(bool validatePositionIncrease,
+                                      bool callPrepareToCloseBeforeJoin) {
+        ASSERT_NO_FATAL_FAILURE(mStream.JoinWorkerAfterBurstCommands(validatePositionIncrease,
+                                                                     callPrepareToCloseBeforeJoin));
+    }
+
+    void SendBurstCommands(bool callPrepareToCloseBeforeJoin, size_t burstCount = 10,
+                           bool standbyInputWhenDone = false) {
+        ASSERT_NO_FATAL_FAILURE(StartWorkerToSendBurstCommands(burstCount, standbyInputWhenDone));
+        // When 'burstCount == 0', there is no "previous" frame count, thus the check for
+        // the position increase fails.
+        ASSERT_NO_FATAL_FAILURE(JoinWorkerAfterBurstCommands(
+                burstCount > 0 /*validatePositionIncrease*/, callPrepareToCloseBeforeJoin));
     }
 
     std::optional<AudioDeviceAddress> getAudioDeviceAddress() const { return mAddress; }
+    const AudioPortConfig& getDevicePortConfig() const { return mStream.getDevicePortConfig(); }
+    int8_t getLastBurstIteration() const { return mStream.getLastData()[0]; }
+    const AudioPortConfig& getPortConfig() const { return mStream.getPortConfig(); }
     std::string skipTestReason() const { return mStream.skipTestReason(); }
 
   private:
-    void SetUp(IModule* module, ModuleConfig* moduleConfig, const AudioPort& devicePort) {}
-
     StreamFixtureWithWorker<Stream> mStream;
     std::optional<AudioDeviceAddress> mAddress;
 };
@@ -4993,77 +5102,130 @@
         }
         ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
     }
+
+    void TearDown() override {
+        streamIn.reset();
+        streamOut.reset();
+    }
+
+    void CreateOutputStream() {
+        streamOut = std::make_unique<WithRemoteSubmix<IStreamOut>>();
+        ASSERT_NO_FATAL_FAILURE(streamOut->SetUp(module.get(), moduleConfig.get()));
+        // Note: any issue with connection attempts is considered as a problem.
+        ASSERT_EQ("", streamOut->skipTestReason());
+        ASSERT_TRUE(streamOut->getAudioDeviceAddress().has_value());
+    }
+
+    void CreateInputStream(const std::optional<AudioDeviceAddress>& address = std::nullopt) {
+        if (address.has_value()) {
+            streamIn = std::make_unique<WithRemoteSubmix<IStreamIn>>(address.value());
+        } else {
+            ASSERT_TRUE(streamOut->getAudioDeviceAddress().has_value());
+            streamIn = std::make_unique<WithRemoteSubmix<IStreamIn>>(
+                    streamOut->getAudioDeviceAddress().value());
+        }
+        ASSERT_NO_FATAL_FAILURE(streamIn->SetUp(module.get(), moduleConfig.get()));
+        ASSERT_EQ("", streamIn->skipTestReason());
+        auto inAddress = streamIn->getAudioDeviceAddress();
+        ASSERT_TRUE(inAddress.has_value());
+        if (address.has_value()) {
+            if (address.value() != AudioDeviceAddress{}) {
+                ASSERT_EQ(address.value(), inAddress.value());
+            }
+        } else {
+            ASSERT_EQ(streamOut->getAudioDeviceAddress().value(), inAddress.value());
+        }
+    }
+
+    std::unique_ptr<WithRemoteSubmix<IStreamOut>> streamOut;
+    std::unique_ptr<WithRemoteSubmix<IStreamIn>> streamIn;
 };
 
 TEST_P(AudioModuleRemoteSubmix, OutputDoesNotBlockWhenNoInput) {
-    WithRemoteSubmix<IStreamOut> streamOut;
-    ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get()));
-    // Note: here and in other tests any issue with connection attempts is considered as a problem.
-    ASSERT_EQ("", streamOut.skipTestReason());
-    ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommands());
+    ASSERT_NO_FATAL_FAILURE(CreateOutputStream());
+    ASSERT_NO_FATAL_FAILURE(streamOut->SendBurstCommands(false /*callPrepareToCloseBeforeJoin*/));
 }
 
-TEST_P(AudioModuleRemoteSubmix, OutputDoesNotBlockWhenInputStuck) {
-    WithRemoteSubmix<IStreamOut> streamOut;
-    ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get()));
-    ASSERT_EQ("", streamOut.skipTestReason());
-    auto address = streamOut.getAudioDeviceAddress();
-    ASSERT_TRUE(address.has_value());
+TEST_P(AudioModuleRemoteSubmix, OutputDoesNotBlockWhenInputInStandby) {
+    if (int32_t version; module->getInterfaceVersion(&version).isOk() && version < 3) {
+        GTEST_SKIP() << "Default remote submix implementation <V3 could not pass this test";
+    }
+    ASSERT_NO_FATAL_FAILURE(CreateOutputStream());
+    ASSERT_NO_FATAL_FAILURE(CreateInputStream());
+    ASSERT_NO_FATAL_FAILURE(streamOut->StartWorkerToSendBurstCommands());
+    // Send just 1 burst command. This triggers the condition "input is in standby after
+    // being active." The output must flush the fifo before writing to avoid being blocked.
+    ASSERT_NO_FATAL_FAILURE(
+            streamIn->StartWorkerToSendBurstCommands(1, true /*stanbyInputWhenDone*/));
+    // The output must be able to close without shutting down the pipe first (due to a call
+    // to 'prepareToClose').
+    ASSERT_NO_FATAL_FAILURE(
+            streamOut->JoinWorkerAfterBurstCommands(false /*callPrepareToCloseBeforeJoin*/));
+    ASSERT_NO_FATAL_FAILURE(
+            streamIn->JoinWorkerAfterBurstCommands(false /*callPrepareToCloseBeforeJoin*/));
+}
 
-    WithRemoteSubmix<IStreamIn> streamIn(address.value());
-    ASSERT_NO_FATAL_FAILURE(streamIn.SetUp(module.get(), moduleConfig.get()));
-    ASSERT_EQ("", streamIn.skipTestReason());
+TEST_P(AudioModuleRemoteSubmix, BlockedOutputUnblocksOnClose) {
+    ASSERT_NO_FATAL_FAILURE(CreateOutputStream());
+    ASSERT_NO_FATAL_FAILURE(CreateInputStream());
+    ASSERT_NO_FATAL_FAILURE(streamOut->StartWorkerToSendBurstCommands());
+    // Send just 3 burst command, but do not enter standby. This is a stalled input.
+    ASSERT_NO_FATAL_FAILURE(streamIn->StartWorkerToSendBurstCommands(3));
+    ASSERT_NO_FATAL_FAILURE(
+            streamOut->JoinWorkerAfterBurstCommands(true /*callPrepareToCloseBeforeJoin*/));
+    ASSERT_NO_FATAL_FAILURE(
+            streamIn->JoinWorkerAfterBurstCommands(false /*callPrepareToCloseBeforeJoin*/));
+}
 
-    ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommands());
+TEST_P(AudioModuleRemoteSubmix, OutputBlocksUntilInputStarts) {
+    ASSERT_NO_FATAL_FAILURE(CreateOutputStream());
+    ASSERT_NO_FATAL_FAILURE(CreateInputStream());
+    ASSERT_NO_FATAL_FAILURE(streamOut->StartWorkerToSendBurstCommands());
+    // Read the head of the pipe and check that it starts with the first output burst, that is,
+    // the contents of the very first write has not been superseded due to pipe overflow.
+    // The burstCount is '0' because the very first burst is used to exit from the 'IDLE' state,
+    // see 'makeBurstCommands'.
+    ASSERT_NO_FATAL_FAILURE(streamIn->SendBurstCommands(false /*callPrepareToCloseBeforeJoin*/, 0,
+                                                        true /*standbyInputWhenDone*/));
+    EXPECT_EQ(1, streamIn->getLastBurstIteration());
+    ASSERT_NO_FATAL_FAILURE(
+            streamOut->JoinWorkerAfterBurstCommands(true /*callPrepareToCloseBeforeJoin*/));
 }
 
 TEST_P(AudioModuleRemoteSubmix, OutputAndInput) {
-    WithRemoteSubmix<IStreamOut> streamOut;
-    ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get()));
-    ASSERT_EQ("", streamOut.skipTestReason());
-    auto address = streamOut.getAudioDeviceAddress();
-    ASSERT_TRUE(address.has_value());
-
-    WithRemoteSubmix<IStreamIn> streamIn(address.value());
-    ASSERT_NO_FATAL_FAILURE(streamIn.SetUp(module.get(), moduleConfig.get()));
-    ASSERT_EQ("", streamIn.skipTestReason());
-
+    ASSERT_NO_FATAL_FAILURE(CreateOutputStream());
+    ASSERT_NO_FATAL_FAILURE(CreateInputStream());
     // Start writing into the output stream.
-    ASSERT_NO_FATAL_FAILURE(streamOut.StartWorkerToSendBurstCommands());
+    ASSERT_NO_FATAL_FAILURE(streamOut->StartWorkerToSendBurstCommands());
     // Simultaneously, read from the input stream.
-    ASSERT_NO_FATAL_FAILURE(streamIn.SendBurstCommands());
-    ASSERT_NO_FATAL_FAILURE(streamOut.JoinWorkerAfterBurstCommands());
+    ASSERT_NO_FATAL_FAILURE(streamIn->SendBurstCommands(false /*callPrepareToCloseBeforeJoin*/));
+    ASSERT_NO_FATAL_FAILURE(
+            streamOut->JoinWorkerAfterBurstCommands(false /*callPrepareToCloseBeforeJoin*/));
 }
 
 TEST_P(AudioModuleRemoteSubmix, OpenInputMultipleTimes) {
-    WithRemoteSubmix<IStreamOut> streamOut;
-    ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get()));
-    ASSERT_EQ("", streamOut.skipTestReason());
-    auto address = streamOut.getAudioDeviceAddress();
-    ASSERT_TRUE(address.has_value());
-
-    const size_t streamInCount = 3;
-    std::vector<std::unique_ptr<WithRemoteSubmix<IStreamIn>>> streamIns(streamInCount);
-    for (size_t i = 0; i < streamInCount; i++) {
-        streamIns[i] = std::make_unique<WithRemoteSubmix<IStreamIn>>(address.value());
-        ASSERT_NO_FATAL_FAILURE(streamIns[i]->SetUp(module.get(), moduleConfig.get()));
+    ASSERT_NO_FATAL_FAILURE(CreateOutputStream());
+    ASSERT_NO_FATAL_FAILURE(CreateInputStream());
+    ASSERT_NO_FATAL_FAILURE(streamOut->StartWorkerToSendBurstCommands());
+    ASSERT_NO_FATAL_FAILURE(streamIn->SendBurstCommands(false /*callPrepareToCloseBeforeJoin*/, 1,
+                                                        true /*standbyInputWhenDone*/));
+    // For the new stream, only create a new mix port config and a new patch.
+    const size_t extraStreamInCount = 2;
+    std::vector<std::unique_ptr<WithRemoteSubmix<IStreamIn>>> streamIns(extraStreamInCount);
+    for (size_t i = 0; i < extraStreamInCount; i++) {
+        streamIns[i] = std::make_unique<WithRemoteSubmix<IStreamIn>>();
+        ASSERT_NO_FATAL_FAILURE(streamIns[i]->SetUp(module.get(), moduleConfig.get(),
+                                                    streamIn->getPortConfig(),
+                                                    streamIn->getDevicePortConfig()));
         ASSERT_EQ("", streamIns[i]->skipTestReason());
+        const auto inAddress = streamIns[i]->getAudioDeviceAddress();
+        ASSERT_TRUE(inAddress.has_value());
+        ASSERT_EQ(streamOut->getAudioDeviceAddress().value(), inAddress.value());
+        ASSERT_NO_FATAL_FAILURE(streamIns[i]->SendBurstCommands(
+                false /*callPrepareToCloseBeforeJoin*/, 1, true /*standbyInputWhenDone*/));
     }
-    // Start writing into the output stream.
-    ASSERT_NO_FATAL_FAILURE(streamOut.StartWorkerToSendBurstCommands());
-    // Simultaneously, read from input streams.
-    for (size_t i = 0; i < streamInCount; i++) {
-        ASSERT_NO_FATAL_FAILURE(streamIns[i]->StartWorkerToSendBurstCommands());
-    }
-    for (size_t i = 0; i < streamInCount; i++) {
-        ASSERT_NO_FATAL_FAILURE(streamIns[i]->JoinWorkerAfterBurstCommands());
-    }
-    ASSERT_NO_FATAL_FAILURE(streamOut.JoinWorkerAfterBurstCommands());
-    // Clean up input streams in the reverse order because the device connection is owned
-    // by the first one.
-    for (size_t i = streamInCount; i != 0; --i) {
-        streamIns[i - 1].reset();
-    }
+    ASSERT_NO_FATAL_FAILURE(
+            streamOut->JoinWorkerAfterBurstCommands(false /*callPrepareToCloseBeforeJoin*/));
 }
 
 INSTANTIATE_TEST_SUITE_P(AudioModuleRemoteSubmixTest, AudioModuleRemoteSubmix,
diff --git a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp
index bf22839..322fdc0 100644
--- a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp
@@ -52,7 +52,7 @@
         AudioChannelLayout::LAYOUT_5POINT1POINT4, AudioChannelLayout::LAYOUT_6POINT1,
         AudioChannelLayout::LAYOUT_7POINT1,       AudioChannelLayout::LAYOUT_7POINT1POINT2,
         AudioChannelLayout::LAYOUT_7POINT1POINT4, AudioChannelLayout::LAYOUT_9POINT1POINT4,
-        AudioChannelLayout::LAYOUT_9POINT1POINT6, AudioChannelLayout::LAYOUT_13POINT_360RA,
+        AudioChannelLayout::LAYOUT_9POINT1POINT6, AudioChannelLayout::LAYOUT_13POINT0,
         AudioChannelLayout::LAYOUT_22POINT2};
 
 static const std::vector<int32_t> kChannels = {
diff --git a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
index 6e8d410..3b1f3d9 100644
--- a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
+++ b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
@@ -116,6 +116,10 @@
 
     bool isAllParamsValid();
 
+    void setParamsAndProcess(std::vector<float>& input, std::vector<float>& output);
+
+    float calculateDb(const std::vector<float>& input, size_t startSamplePos);
+
     // enqueue test parameters
     void addEngineConfig(const DynamicsProcessing::EngineArchitecture& cfg);
     void addPreEqChannelConfig(const std::vector<DynamicsProcessing::ChannelConfig>& cfg);
@@ -131,6 +135,9 @@
     static constexpr int kBandCount = 5;
     static constexpr int kSamplingFrequency = 44100;
     static constexpr int kFrameCount = 2048;
+    static constexpr int kInputFrequency = 1000;
+    static constexpr size_t kStartIndex = 15 * kSamplingFrequency / 1000;  // skip 15ms
+    static constexpr float kToleranceDb = 0.05;
     std::shared_ptr<IFactory> mFactory;
     std::shared_ptr<IEffect> mEffect;
     Descriptor mDescriptor;
@@ -390,6 +397,22 @@
     return true;
 }
 
+float DynamicsProcessingTestHelper::calculateDb(const std::vector<float>& input,
+                                                size_t startSamplePos = 0) {
+    return audio_utils_compute_power_mono(input.data() + startSamplePos, AUDIO_FORMAT_PCM_FLOAT,
+                                          input.size() - startSamplePos);
+}
+
+void DynamicsProcessingTestHelper::setParamsAndProcess(std::vector<float>& input,
+                                                       std::vector<float>& output) {
+    ASSERT_NO_FATAL_FAILURE(SetAndGetDynamicsProcessingParameters());
+    if (isAllParamsValid()) {
+        ASSERT_NO_FATAL_FAILURE(
+                processAndWriteToOutput(input, output, mEffect, &mOpenEffectReturn));
+        ASSERT_GT(output.size(), kStartIndex);
+    }
+}
+
 void DynamicsProcessingTestHelper::addEngineConfig(
         const DynamicsProcessing::EngineArchitecture& cfg) {
     DynamicsProcessing dp;
@@ -593,6 +616,66 @@
         });
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingTestInputGain);
 
+class DynamicsProcessingInputGainDataTest
+    : public ::testing::TestWithParam<std::pair<std::shared_ptr<IFactory>, Descriptor>>,
+      public DynamicsProcessingTestHelper {
+  public:
+    DynamicsProcessingInputGainDataTest()
+        : DynamicsProcessingTestHelper((GetParam()), AudioChannelLayout::LAYOUT_MONO) {
+        mInput.resize(kFrameCount * mChannelCount);
+        generateSineWave(kInputFrequency /*Input Frequency*/, mInput);
+        mInputDb = calculateDb(mInput);
+    }
+
+    void SetUp() override {
+        SetUpDynamicsProcessingEffect();
+        SKIP_TEST_IF_DATA_UNSUPPORTED(mDescriptor.common.flags);
+    }
+
+    void TearDown() override { TearDownDynamicsProcessingEffect(); }
+
+    void cleanUpInputGainConfig() {
+        CleanUp();
+        mInputGain.clear();
+    }
+
+    std::vector<DynamicsProcessing::InputGain> mInputGain;
+    std::vector<float> mInput;
+    float mInputDb;
+};
+
+TEST_P(DynamicsProcessingInputGainDataTest, SetAndGetInputGain) {
+    std::vector<float> gainDbValues = {-85, -40, 0, 40, 85};
+    for (float gainDb : gainDbValues) {
+        cleanUpInputGainConfig();
+        for (int i = 0; i < mChannelCount; i++) {
+            mInputGain.push_back(DynamicsProcessing::InputGain(i, gainDb));
+        }
+        std::vector<float> output(mInput.size());
+        EXPECT_NO_FATAL_FAILURE(addInputGain(mInputGain));
+        EXPECT_NO_FATAL_FAILURE(setParamsAndProcess(mInput, output));
+        if (!isAllParamsValid()) {
+            continue;
+        }
+        float outputDb = calculateDb(output, kStartIndex);
+        EXPECT_NEAR(outputDb, mInputDb + gainDb, kToleranceDb)
+                << "InputGain: " << gainDb << ", OutputDb: " << outputDb;
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(DynamicsProcessingTest, DynamicsProcessingInputGainDataTest,
+                         testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+                                 IFactory::descriptor, getEffectTypeUuidDynamicsProcessing())),
+                         [](const auto& info) {
+                             auto descriptor = info.param;
+                             std::string name = getPrefix(descriptor.second);
+                             std::replace_if(
+                                     name.begin(), name.end(),
+                                     [](const char c) { return !std::isalnum(c); }, '_');
+                             return name;
+                         });
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingInputGainDataTest);
+
 /**
  * Test DynamicsProcessing Limiter Config
  */
@@ -686,11 +769,6 @@
 
     void TearDown() override { TearDownDynamicsProcessingEffect(); }
 
-    float calculateDb(std::vector<float> input, size_t start = 0) {
-        return audio_utils_compute_power_mono(input.data() + start, AUDIO_FORMAT_PCM_FLOAT,
-                                              input.size() - start);
-    }
-
     void computeThreshold(float ratio, float outputDb, float& threshold) {
         EXPECT_NE(ratio, 0);
         threshold = (mInputDb - (ratio * outputDb)) / (1 - ratio);
@@ -703,16 +781,10 @@
         ratio = inputOverThreshold / outputOverThreshold;
     }
 
-    void setParamsAndProcess(std::vector<float>& output) {
+    void setLimiterParamsAndProcess(std::vector<float>& input, std::vector<float>& output) {
         EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset));
         EXPECT_NO_FATAL_FAILURE(addLimiterConfig(mLimiterConfigList));
-        ASSERT_NO_FATAL_FAILURE(SetAndGetDynamicsProcessingParameters());
-        if (isAllParamsValid()) {
-            ASSERT_NO_FATAL_FAILURE(
-                    processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn));
-            EXPECT_GT(output.size(), kStartIndex);
-        }
-        cleanUpLimiterConfig();
+        EXPECT_NO_FATAL_FAILURE(setParamsAndProcess(input, output));
     }
 
     void cleanUpLimiterConfig() {
@@ -723,10 +795,8 @@
     static constexpr float kDefaultAttackTime = 0;
     static constexpr float kDefaultReleaseTime = 0;
     static constexpr float kDefaultRatio = 4;
-    static constexpr float kDefaultThreshold = 0;
+    static constexpr float kDefaultThreshold = -10;
     static constexpr float kDefaultPostGain = 0;
-    static constexpr int kInputFrequency = 1000;
-    static constexpr size_t kStartIndex = 15 * kSamplingFrequency / 1000;  // skip 15ms
     std::vector<DynamicsProcessing::LimiterConfig> mLimiterConfigList;
     std::vector<float> mInput;
     float mInputDb;
@@ -738,17 +808,18 @@
     std::vector<float> output(mInput.size());
     float previousThreshold = -FLT_MAX;
     for (float threshold : thresholdValues) {
+        cleanUpLimiterConfig();
         for (int i = 0; i < mChannelCount; i++) {
             fillLimiterConfig(mLimiterConfigList, i, true, kDefaultLinkerGroup, kDefaultAttackTime,
                               kDefaultReleaseTime, kDefaultRatio, threshold, kDefaultPostGain);
         }
-        EXPECT_NO_FATAL_FAILURE(setParamsAndProcess(output));
+        EXPECT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
         if (!isAllParamsValid()) {
             continue;
         }
         float outputDb = calculateDb(output, kStartIndex);
         if (threshold >= mInputDb || kDefaultRatio == 1) {
-            EXPECT_EQ(std::round(mInputDb), std::round(outputDb));
+            EXPECT_NEAR(mInputDb, outputDb, kToleranceDb);
         } else {
             float calculatedThreshold = 0;
             EXPECT_NO_FATAL_FAILURE(computeThreshold(kDefaultRatio, outputDb, calculatedThreshold));
@@ -761,48 +832,68 @@
 TEST_P(DynamicsProcessingLimiterConfigDataTest, IncreasingRatio) {
     std::vector<float> ratioValues = {1, 10, 20, 30, 40, 50};
     std::vector<float> output(mInput.size());
-    float threshold = -10;
     float previousRatio = 0;
     for (float ratio : ratioValues) {
+        cleanUpLimiterConfig();
         for (int i = 0; i < mChannelCount; i++) {
             fillLimiterConfig(mLimiterConfigList, i, true, kDefaultLinkerGroup, kDefaultAttackTime,
-                              kDefaultReleaseTime, ratio, threshold, kDefaultPostGain);
+                              kDefaultReleaseTime, ratio, kDefaultThreshold, kDefaultPostGain);
         }
-        EXPECT_NO_FATAL_FAILURE(setParamsAndProcess(output));
+        EXPECT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
         if (!isAllParamsValid()) {
             continue;
         }
         float outputDb = calculateDb(output, kStartIndex);
 
-        if (threshold >= mInputDb) {
-            EXPECT_EQ(std::round(mInputDb), std::round(outputDb));
+        if (kDefaultThreshold >= mInputDb) {
+            EXPECT_NEAR(mInputDb, outputDb, kToleranceDb);
         } else {
             float calculatedRatio = 0;
-            EXPECT_NO_FATAL_FAILURE(computeRatio(threshold, outputDb, calculatedRatio));
+            EXPECT_NO_FATAL_FAILURE(computeRatio(kDefaultThreshold, outputDb, calculatedRatio));
             ASSERT_GT(calculatedRatio, previousRatio);
             previousRatio = calculatedRatio;
         }
     }
 }
 
+TEST_P(DynamicsProcessingLimiterConfigDataTest, IncreasingPostGain) {
+    std::vector<float> postGainDbValues = {-85, -40, 0, 40, 85};
+    std::vector<float> output(mInput.size());
+    for (float postGainDb : postGainDbValues) {
+        cleanUpLimiterConfig();
+        for (int i = 0; i < mChannelCount; i++) {
+            fillLimiterConfig(mLimiterConfigList, i, true, kDefaultLinkerGroup, kDefaultAttackTime,
+                              kDefaultReleaseTime, kDefaultRatio, -1, postGainDb);
+        }
+        EXPECT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
+        if (!isAllParamsValid()) {
+            continue;
+        }
+        float outputDb = calculateDb(output, kStartIndex);
+        EXPECT_NEAR(outputDb, mInputDb + postGainDb, kToleranceDb)
+                << "PostGain: " << postGainDb << ", OutputDb: " << outputDb;
+    }
+}
+
 TEST_P(DynamicsProcessingLimiterConfigDataTest, LimiterEnableDisable) {
     std::vector<bool> limiterEnableValues = {false, true};
     std::vector<float> output(mInput.size());
     for (bool isEnabled : limiterEnableValues) {
+        cleanUpLimiterConfig();
         for (int i = 0; i < mChannelCount; i++) {
             // Set non-default values
             fillLimiterConfig(mLimiterConfigList, i, isEnabled, kDefaultLinkerGroup,
                               5 /*attack time*/, 5 /*release time*/, 10 /*ratio*/,
                               -10 /*threshold*/, 5 /*postgain*/);
         }
-        EXPECT_NO_FATAL_FAILURE(setParamsAndProcess(output));
+        EXPECT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
         if (!isAllParamsValid()) {
             continue;
         }
         if (isEnabled) {
             EXPECT_NE(mInputDb, calculateDb(output, kStartIndex));
         } else {
-            EXPECT_NEAR(mInputDb, calculateDb(output, kStartIndex), 0.05);
+            EXPECT_NEAR(mInputDb, calculateDb(output, kStartIndex), kToleranceDb);
         }
     }
 }
diff --git a/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp b/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
index a29920e..bf48a87 100644
--- a/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
@@ -43,13 +43,13 @@
 static const std::vector<TagVectorPair> kParamsIncreasingVector = {
         {EnvironmentalReverb::roomLevelMb, {-3500, -2800, -2100, -1400, -700, 0}},
         {EnvironmentalReverb::roomHfLevelMb, {-4000, -3200, -2400, -1600, -800, 0}},
-        {EnvironmentalReverb::decayTimeMs, {800, 1600, 2400, 3200, 4000}},
-        {EnvironmentalReverb::decayHfRatioPm, {100, 600, 1100, 1600, 2000}},
+        {EnvironmentalReverb::decayTimeMs, {400, 800, 1200, 1600, 2000}},
+        {EnvironmentalReverb::decayHfRatioPm, {1000, 900, 800, 700}},
         {EnvironmentalReverb::levelMb, {-3500, -2800, -2100, -1400, -700, 0}},
 };
 
 static const TagVectorPair kDiffusionParam = {EnvironmentalReverb::diffusionPm,
-                                              {200, 400, 600, 800, 1000}};
+                                              {100, 300, 500, 700, 900}};
 static const TagVectorPair kDensityParam = {EnvironmentalReverb::densityPm,
                                             {0, 200, 400, 600, 800, 1000}};
 
@@ -281,7 +281,7 @@
 
     static constexpr int kDurationMilliSec = 500;
     static constexpr int kBufferSize = kSamplingFrequency * kDurationMilliSec / 1000;
-    static constexpr int kInputFrequency = 1000;
+    static constexpr int kInputFrequency = 2000;
 
     int mStereoChannelCount =
             getChannelCount(AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
diff --git a/audio/policy/1.0/xml/audio_policy_engine_configuration.xsd b/audio/policy/1.0/xml/audio_policy_engine_configuration.xsd
index 02e593a..2b86049 100644
--- a/audio/policy/1.0/xml/audio_policy_engine_configuration.xsd
+++ b/audio/policy/1.0/xml/audio_policy_engine_configuration.xsd
@@ -105,7 +105,7 @@
                         <xs:element name="AttributesGroup" type="AttributesGroup" minOccurs="1" maxOccurs="unbounded"/>
                     </xs:sequence>
                     <xs:attribute name="name" type="xs:string" use="required"/>
-                    <xs:attribute name="id" type="xs:int" use="required"/>
+                    <xs:attribute name="id" type="xs:int" use="optional"/>
                 </xs:complexType>
             </xs:element>
         </xs:sequence>
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl
index 220b1da..cdf9066 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl
@@ -237,6 +237,8 @@
      * {@link StatusCode#INVALID_ARG}. If a specified propId was not subscribed
      * before, this method must ignore that propId.
      *
+     * Unsubscribe a not-subscribed property ID must do nothing.
+     *
      * If error is returned, some of the properties failed to unsubscribe.
      * Caller is safe to try again, since unsubscribing an already unsubscribed
      * property is okay.
@@ -267,6 +269,9 @@
     /**
      * Gets the supported values lists for [propId, areaId]s.
      *
+     * This is only supported for [propId, areaId]s that have non-null
+     * {@code hasSupportedValueInfo} for their {@code VehicleAreaConfig}.
+     *
      * For a specific [propId, areaId], if the hardware currently specifies
      * a list of supported values for it, then it is returned through the
      * {@code supportedValuesList} field inside
@@ -304,6 +309,9 @@
     /**
      * Gets the min/max supported values for [propId, areaId]s.
      *
+     * This is only supported for [propId, areaId]s that have non-null
+     * {@code hasSupportedValueInfo} for their {@code VehicleAreaConfig}.
+     *
      * For a specific [propId, areaId], if the hardware currently specifies
      * min/max supported value for it, then it is returned through the
      * {@code minSupportedValue} or {@code maxSupportedValue} field inside
@@ -342,6 +350,9 @@
     /**
      * Registers the supported value change callback.
      *
+     * This is only supported for [propId, areaId]s that have non-null
+     * {@code hasSupportedValueInfo} for their {@code VehicleAreaConfig}.
+     *
      * For the specified [propId, areaId]s,
      * {@code callback.onSupportedValueChange} must be invoked if change
      * happens.
@@ -360,6 +371,9 @@
     /**
      * Unregisters the supported value change callback.
      *
+     * This is only supported for [propId, areaId]s that have non-null
+     * {@code hasSupportedValueInfo} for their {@code VehicleAreaConfig}.
+     *
      * @param callback The callback to unregister.
      * @param propIdAreaIds A list of [propId, areaId]s to unregister.
      */
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.aidl
index f85ad3a..a3508ee 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.aidl
@@ -43,6 +43,8 @@
      *
      * If the [propId, areaId] does not specify a max supported value, this
      * is {@code null}.
+     *
+     * This must be ignored if status is not {@code StatusCode.OK}.
      */
     @nullable RawPropValues maxSupportedValue;
 }
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SupportedValuesListResult.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SupportedValuesListResult.aidl
index 4524f4f..8800b0b 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SupportedValuesListResult.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SupportedValuesListResult.aidl
@@ -36,6 +36,8 @@
      *
      * If the [propId, areaId] does not specify a supported values list, this
      * is {@code null}.
+     *
+     * This must be ignored if status is not {@code StatusCode.OK}.
      */
     @nullable List<RawPropValues> supportedValuesList;
 }
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
index 62d7e21..c6b8cd1 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
@@ -230,10 +230,17 @@
     boolean supportVariableUpdateRate;
 
     /**
-     * For VHAL implementation >= V4, this must not be {@code null}. This specifies whether
-     * this property may have min/max supported value or supported values list.
+     * This specifies whether this property may have min/max supported value or supported values
+     * list for [propId, areaId] that supports new supported values APIs.
      *
-     * For VHAL implementation < V4, this is always {@code null} and is not accessed.
+     * If this is not {@code null}. The client may use {@code getMinMaxSupportedValue},
+     * {@code getSupportedValuesLists}, {@code subscribeSupportedValueChange},
+     * {@code unsubscribeSupportedValueChange}.
+     *
+     * If this is {@code null} for legacy properties, the APIs mentioned before are not supported.
+     * Client must fallback to use static supported value information in {@code VehicleAreaConfig}.
+     *
+     * For VHAL implementation < V4, this is always {@code null}.
      */
     @nullable HasSupportedValueInfo hasSupportedValueInfo;
 }
diff --git a/automotive/vehicle/aidl/emu_metadata/android.hardware.automotive.vehicle-types-meta.json b/automotive/vehicle/aidl/emu_metadata/android.hardware.automotive.vehicle-types-meta.json
index 272f7c5..b774580 100644
--- a/automotive/vehicle/aidl/emu_metadata/android.hardware.automotive.vehicle-types-meta.json
+++ b/automotive/vehicle/aidl/emu_metadata/android.hardware.automotive.vehicle-types-meta.json
@@ -218,7 +218,7 @@
             {
                 "name": "Tire pressure",
                 "value": 392168201,
-                "description": "Tire pressure\n{@code HasSupportedValueInfo.hasMinSupportedValue} and {@code HasSupportedValueInfo.hasMaxSupportedValue} must be {@code true} for all areas.\n{@code MinMaxSupportedValueResult.minSupportedValue} represents the lower bound of the recommended tire pressure for the tire at the specified area ID.\n{@code MinMaxSupportedValueResult.maxSupportedValue} represents the upper bound of the recommended tire pressure for the tire at the specified area ID.\nFor example, if the recommended tire pressure of left_front tire is from 200.0 KILOPASCAL to 240.0 KILOPASCAL, {@code getMinMaxSupportedValue} for [propId=TIRE_PRESSURE, areaId=VehicleAreaWheel::LEFT_FRONT] must return a {@code MinMaxSupportedValueResult} with OK status, 200.0 as minSupportedValue, 240.0 as maxSupportedValue.\nFor backward compatibility, minFloatValue and maxFloatValue in {@code VehicleAreaConfig} must be set to the same as minSupportedValue and maxSupportedValue at boot time.\nEach tire is identified by its areaConfig.areaId config.\nFor example: .areaConfigs = { VehicleAreaConfig { .areaId = VehicleAreaWheel::LEFT_FRONT, .minFloatValue = 200.0, .maxFloatValue = 240.0, } },"
+                "description": "Tire pressure\nEach tire is identified by its areaConfig.areaId config and its minFloatValue\/maxFloatValue are used to store OEM recommended pressure range.\nThe minFloatValue and maxFloatValue in VehicleAreaConfig must be defined.\nThe minFloatValue in the areaConfig data represents the lower bound of the recommended tire pressure.\nThe maxFloatValue in the areaConfig data represents the upper bound of the recommended tire pressure.\nFor example:\nThe following areaConfig indicates the recommended tire pressure of the left_front tire is from 200.0 KILOPASCAL to 240.0 KILOPASCAL. .areaConfigs = { VehicleAreaConfig { .areaId = VehicleAreaWheel::LEFT_FRONT, .minFloatValue = 200.0, .maxFloatValue = 240.0, } }\nIf {@code HasSupportedValueInfo} for a specific area ID is not {@code null}:\n{@code HasSupportedValueInfo.hasMinSupportedValue} and {@code HasSupportedValueInfo.hasMaxSupportedValue} must be {@code true} for the area ID.\n{@code MinMaxSupportedValueResult.minSupportedValue} represents the lower bound of the recommended tire pressure for the tire at the specified area ID.\n{@code MinMaxSupportedValueResult.maxSupportedValue} represents the upper bound of the recommended tire pressure for the tire at the specified area ID.\nFor example, if the recommended tire pressure of left_front tire is from 200.0 KILOPASCAL to 240.0 KILOPASCAL, {@code getMinMaxSupportedValue} for [propId=TIRE_PRESSURE, areaId=VehicleAreaWheel::LEFT_FRONT] must return a {@code MinMaxSupportedValueResult} with OK status, 200.0 as minSupportedValue, 240.0 as maxSupportedValue.\nAt boot, minFloatValue is equal to minSupportedValue, maxFloatValue is equal to maxSupportedValue."
             },
             {
                 "name": "Critically low tire pressure",
@@ -262,7 +262,7 @@
                     "ImpactSensorLocation"
                 ],
                 "data_enum": "ImpactSensorLocation",
-                "description": "Impact detected.\nBit flag property to relay information on whether an impact has occurred on a particular side of the vehicle as described through the ImpactSensorLocation enum. As a bit flag property, this property can be set to multiple ORed together values of the enum when necessary.\nFor the global area ID (0), {@code getSupportedValuesList} must return a {@code SupportedValuesListResult} that contains supported values unless all bit flags of ImpactSensorLocation are supported.\nFor backward compatibility, if {@code SupportedValuesListResult} is defined, {@code VehicleAreaConfig#supportedEnumValues} must be set to the same values."
+                "description": "Impact detected.\nBit flag property to relay information on whether an impact has occurred on a particular side of the vehicle as described through the ImpactSensorLocation enum. As a bit flag property, this property can be set to multiple ORed together values of the enum when necessary.\nFor the global area ID (0), the VehicleAreaConfig#supportedEnumValues array must be defined unless all bit flags of ImpactSensorLocation are supported.\nIf {@code HasSupportedValueInfo} is not {@code null} for the global area ID (0):\n{@code getSupportedValuesList} must return a {@code SupportedValuesListResult} that contains supported values unless all bit flags of ImpactSensorLocation are supported.\nAt boot, supportedEnumValues is equal to the supported values list."
             },
             {
                 "name": "Vehicle horn engaged.",
@@ -276,7 +276,7 @@
                     "VehicleGear"
                 ],
                 "data_enum": "VehicleGear",
-                "description": "Currently selected gear\nThis is the gear selected by the user.\n{@code VehicleAreaConfig.HasSupportedValueInfo.hasSupportedValuesList} for the global area ID (0) must be {@code true}.\n{@code getSupportedValuesList} for [GEAR_SELECTION, areaId=0] must return a {@code SupportedValuesListResult} that contains non-null {@code supportedValuesList}.\nThe supportedValues must represent the list of supported gears for this vehicle. For example, for an automatic transmission, the list can be {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_PARK, GEAR_DRIVE, GEAR_1, GEAR_2,...} and for manual transmission it can be {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_1, GEAR_2,...}.\nIn the case of an automatic transmission vehicle that allows the driver to select specific gears on demand (i.e. \"manual mode\"), GEAR_SELECTION's value must be set to the specific gear selected by the driver instead of simply GEAR_DRIVE.\nFor backward compatibility, config array for this property must be a list of values same as the supported values at boot-time."
+                "description": "Currently selected gear\nThis is the gear selected by the user.\nValues in the config array must represent the list of supported gears for this vehicle at boot time. For example, config array for an automatic transmission must contain {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_PARK, GEAR_DRIVE, GEAR_1, GEAR_2,...} and for manual transmission the list must contain {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_1, GEAR_2,...}\nIn the case of an automatic transmission vehicle that allows the driver to select specific gears on demand (i.e. \"manual mode\"), GEAR_SELECTION's value must be set to the specific gear selected by the driver instead of simply GEAR_DRIVE.\nIf {@code HasSupportedValueInfo} is not {@code null} for the global area ID (0):\n{@code VehicleAreaConfig.HasSupportedValueInfo.hasSupportedValuesList} for the global area ID (0) must be {@code true}.\n{@code getSupportedValuesList} for [GEAR_SELECTION, areaId=0] must return a {@code SupportedValuesListResult} that contains non-null {@code supportedValuesList}.\nThe supportedValues must represent the list of supported gears for this vehicle. For example, for an automatic transmission, the list can be {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_PARK, GEAR_DRIVE, GEAR_1, GEAR_2,...} and for manual transmission it can be {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_1, GEAR_2,...}.\nIn the case of an automatic transmission vehicle that allows the driver to select specific gears on demand (i.e. \"manual mode\"), the GEAR_SELECTION property value must be set to the specific gear selected by the driver instead of simply GEAR_DRIVE.\nAt boot, the config array's values are equal to the supported values list."
             },
             {
                 "name": "CURRENT_GEAR",
@@ -285,7 +285,7 @@
                     "VehicleGear"
                 ],
                 "data_enum": "VehicleGear",
-                "description": "Current gear. In non-manual case, selected gear may not match the current gear. For example, if the selected gear is GEAR_DRIVE, the current gear will be one of GEAR_1, GEAR_2 etc, which reflects the actual gear the transmission is currently running in.\n{@code VehicleAreaConfig.HasSupportedValueInfo.hasSupportedValuesList} for the global area ID (0) must be {@code true}.\n{@code getSupportedValuesList} for [GEAR_SELECTION, areaId=0] must return a {@code SupportedValuesListResult} that contains non-null {@code supportedValuesList}.\nThe supported values list must represent the list of supported gears for this vehicle.  For example, for an automatic transmission, this list can be {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_PARK, GEAR_1, GEAR_2,...} and for manual transmission the list can be {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_1, GEAR_2,...}. This list need not be the same as that of the supported gears reported in GEAR_SELECTION.\nFor backward compatibility, config array for this property must be a list of values same as the supported values at boot-time."
+                "description": "Current gear. In non-manual case, selected gear may not match the current gear. For example, if the selected gear is GEAR_DRIVE, the current gear will be one of GEAR_1, GEAR_2 etc, which reflects the actual gear the transmission is currently running in.\nValues in the config array must represent the list of supported gears for this vehicle at boot time.  For example, config array for an automatic transmission must contain {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_PARK, GEAR_1, GEAR_2,...} and for manual transmission the list must contain {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_1, GEAR_2,...}. This list need not be the same as that of the supported gears reported in GEAR_SELECTION.\nIf {@code HasSupportedValueInfo} is not {@code null} for the global area ID (0):\n{@code VehicleAreaConfig.HasSupportedValueInfo.hasSupportedValuesList} for the global area ID (0) must be {@code true}.\n{@code getSupportedValuesList} for [GEAR_SELECTION, areaId=0] must return a {@code SupportedValuesListResult} that contains non-null {@code supportedValuesList}.\nThe supported values list must represent the list of supported gears for this vehicle.  For example, for an automatic transmission, this list can be {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_PARK, GEAR_1, GEAR_2,...} and for manual transmission the list can be {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_1, GEAR_2,...}. This list need not be the same as that of the supported gears reported in GEAR_SELECTION.\nAt boot, the config array's values are equal to the supported values list."
             },
             {
                 "name": "Parking brake state.",
@@ -300,7 +300,7 @@
             {
                 "name": "EV_BRAKE_REGENERATION_LEVEL",
                 "value": 289408012,
-                "description": "Regenerative braking level of a electronic vehicle\n{@code HasSupportedValueInfo.hasMinSupportedValue} and {@code HasSupportedValueInfo.hasMaxSupportedValue} must be {@code true} for global area ID(0)\n{@code MinMaxSupportedValueResult.minSupportedValue} must be 0.\n{@code MinMaxSupportedValueResult.maxSupportedValue} indicates the setting for the maximum amount of energy regenerated from braking. The minSupportedValue indicates the setting for no regenerative braking.\nAll values between min and max supported value must be supported.\nFor backward compatibility, minInt32Value and maxInt32Value in {@code VehicleAreaConfig} must be set to the same as minSupportedValue and maxSupportedValue at boot time.\nThis property is a more granular form of EV_REGENERATIVE_BRAKING_STATE. It allows the user to set a more specific level of regenerative braking if the states in EvRegenerativeBrakingState are not granular enough for the OEM.\nThis property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to implement it as VehiclePropertyAccess.READ only."
+                "description": "Regenerative braking level of a electronic vehicle\nThe minInt32Value and maxInt32Value in VehicleAreaConfig must be defined. All values between minInt32Value and maxInt32Value must be supported.\nThe minInt32Value indicates the setting for no regenerative braking, must be 0.\nThe maxInt32Value indicates the setting for the maximum amount of energy regenerated from braking.\nAll values between min and max supported value must be supported.\nIf {@code HasSupportedValueInfo} is not {@code null} for the global area ID (0):\n{@code HasSupportedValueInfo.hasMinSupportedValue} and {@code HasSupportedValueInfo.hasMaxSupportedValue} must be {@code true} for global area ID(0)\n{@code MinMaxSupportedValueResult.minSupportedValue} must be 0.\n{@code MinMaxSupportedValueResult.maxSupportedValue} indicates the setting for the maximum amount of energy regenerated from braking. The minSupportedValue indicates the setting for no regenerative braking.\nAt boot, minInt32Value is equal to minSupportedValue, maxInt32Value is equal to maxSupportedValue.\n\nThis property is a more granular form of EV_REGENERATIVE_BRAKING_STATE. It allows the user to set a more specific level of regenerative braking if the states in EvRegenerativeBrakingState are not granular enough for the OEM.\nThis property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to implement it as VehiclePropertyAccess.READ only."
             },
             {
                 "name": "Warning for fuel low level.",
@@ -347,7 +347,7 @@
                     "EvStoppingMode"
                 ],
                 "data_enum": "EvStoppingMode",
-                "description": "Represents property for the current stopping mode of the vehicle.\nFor the global area ID (0), {@code getSupportedValuesList} must return a {@code SupportedValuesListResult} that contains supported values unless all enum values of EvStoppingMode are supported.\nFor backward compatibility, if {@code SupportedValuesListResult} is defined, {@code VehicleAreaConfig#supportedEnumValues} must be set to the same values.\nThe EvStoppingMode enum may be extended to include more states in the future.\nThis property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to implement it as VehiclePropertyAccess.READ only."
+                "description": "Represents property for the current stopping mode of the vehicle.\nFor the global area ID (0), the VehicleAreaConfig#supportedEnumValues must be defined unless all enum values of EvStoppingMode are supported.\nIf {@code HasSupportedValueInfo} is not {@code null} for the global area ID (0):\nFor the global area ID (0), {@code getSupportedValuesList} must return a {@code SupportedValuesListResult} that contains supported values unless all enum values of EvStoppingMode are supported.\nAt boot, supportedEnumValues is equal to the supported values list.\n\nThe EvStoppingMode enum may be extended to include more states in the future.\nThis property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to implement it as VehiclePropertyAccess.READ only."
             },
             {
                 "name": "ELECTRONIC_STABILITY_CONTROL_ENABLED",
@@ -362,7 +362,7 @@
                     "ErrorState"
                 ],
                 "data_enum": "ElectronicStabilityControlState",
-                "description": "Electronic Stability Control (ESC) state.\nReturns the current state of ESC. This property must always return a valid state defined in ElectronicStabilityControlState or ErrorState. It must not surface errors through StatusCode and must use the supported error states instead.\nFor the global area ID (0), {@code getSupportedValuesList} must return a {@code SupportedValuesListResult} that contains supported values unless all states of both ElectronicStabilityControlState (including OTHER, which is not recommended) and ErrorState are supported.\nFor backward compatibility, if {@code SupportedValuesListResult} is defined, {@code VehicleAreaConfig#supportedEnumValues} must be set to the same values."
+                "description": "Electronic Stability Control (ESC) state.\nReturns the current state of ESC. This property must always return a valid state defined in ElectronicStabilityControlState or ErrorState. It must not surface errors through StatusCode and must use the supported error states instead.\nFor the global area ID (0), the VehicleAreaConfig#supportedEnumValues array must be defined unless all states of both ElectronicStabilityControlState (including OTHER, which is not recommended) and ErrorState are supported.\nIf {@code HasSupportedValueInfo} is not {@code null} for the global area ID (0):\nFor the global area ID (0), {@code getSupportedValuesList} must return a {@code SupportedValuesListResult} that contains supported values unless all states of both ElectronicStabilityControlState (including OTHER, which is not recommended) and ErrorState are supported.\nAt boot, supportedEnumValues is equal to the supported values list."
             },
             {
                 "name": "Turn signal light state.",
@@ -385,7 +385,7 @@
             {
                 "name": "Fan speed setting",
                 "value": 356517120,
-                "description": "Fan speed setting\nThe maxInt32Value and minInt32Value in VehicleAreaConfig must be defined. All integers between minInt32Value and maxInt32Value must be supported.\nThe minInt32Value indicates the lowest fan speed. The maxInt32Value indicates the highest fan speed.\nThis property is not in any particular unit but in a specified range of relative speeds.\nThis property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to implement it as VehiclePropertyAccess.READ only."
+                "description": "Fan speed setting\nThe maxInt32Value and minInt32Value in VehicleAreaConfig must be defined. All integers between minInt32Value and maxInt32Value must be supported.\nThe minInt32Value indicates the lowest fan speed. The maxInt32Value indicates the highest fan speed.\nIf {@code HasSupportedValueInfo} for a specific area ID is not {@code null}:\n{@code HasSupportedValueInfo.hasMinSupportedValue} and {@code HasSupportedValueInfo.hasMaxSupportedValue} must be {@code true} for the specific area ID.\n{@code MinMaxSupportedValueResult.minSupportedValue} indicates the lowest fan speed.\n{@code MinMaxSupportedValueResult.maxSupportedValue} indicates the highest fan speed.\nAll integers between minSupportedValue and maxSupportedValue must be supported.\nAt boot, minInt32Value is equal to minSupportedValue, maxInt32Value is equal to maxSupportedValue.\n\nThis property is not in any particular unit but in a specified range of relative speeds.\nThis property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to implement it as VehiclePropertyAccess.READ only."
             },
             {
                 "name": "Fan direction setting",
@@ -552,7 +552,7 @@
                     "VehicleUnit"
                 ],
                 "data_enum": "VehicleUnit",
-                "description": "Speed units for display\nIndicates type of units the car is using to display speed to user. Eg. m\/s, km\/h, or mph.\n{@code VehicleAreaConfig.HasSupportedValueInfo.hasSupportedValuesList} for the global area ID (0) must be {@code true}.\n{@code getSupportedValuesLists} for [VEHICLE_SPEED_DISPLAY_UNITS, areaId=0] must return a {@code SupportedValuesListResult} that contains non-null {@code supportedValuesList}, e.g. [METER_PER_SEC, MILES_PER_HOUR, KILOMETERS_PER_HOUR].\nFor backward compatibility, config array for this property must contain the same values as supported values at boot time. For example: configArray[0] = METER_PER_SEC configArray[1] = MILES_PER_HOUR configArray[2] = KILOMETERS_PER_HOUR\nIf updating VEHICLE_SPEED_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS properties, then their values must be updated and communicated to the AAOS framework as well.\nThis property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to implement it as VehiclePropertyAccess.READ only."
+                "description": "Speed units for display\nIndicates type of units the car is using to display speed to user. Eg. m\/s, km\/h, or mph.\nVehiclePropConfig.configArray is used to indicate the supported speed display units. Pressure units are defined in VehicleUnit. For example: configArray[0] = METER_PER_SEC configArray[1] = MILES_PER_HOUR configArray[2] = KILOMETERS_PER_HOUR\nIf {@code HasSupportedValueInfo} is not {@code null} for the global area ID (0):\n{@code VehicleAreaConfig.HasSupportedValueInfo.hasSupportedValuesList} for the global area ID (0) must be {@code true}.\n{@code getSupportedValuesLists} for [VEHICLE_SPEED_DISPLAY_UNITS, areaId=0] must return a {@code SupportedValuesListResult} that contains non-null {@code supportedValuesList}, e.g. [METER_PER_SEC, MILES_PER_HOUR, KILOMETERS_PER_HOUR].\nAt boot, the values in the config array are equal to the supported values list.\n\nIf updating VEHICLE_SPEED_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS properties, then their values must be updated and communicated to the AAOS framework as well.\nThis property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to implement it as VehiclePropertyAccess.READ only."
             },
             {
                 "name": "EXTERNAL_CAR_TIME",
diff --git a/automotive/vehicle/aidl/impl/3/utils/common/test/PendingRequestPoolTest.cpp b/automotive/vehicle/aidl/impl/3/utils/common/test/PendingRequestPoolTest.cpp
index 734c739..cc690d7 100644
--- a/automotive/vehicle/aidl/impl/3/utils/common/test/PendingRequestPoolTest.cpp
+++ b/automotive/vehicle/aidl/impl/3/utils/common/test/PendingRequestPoolTest.cpp
@@ -57,7 +57,7 @@
 
   private:
     // Test timeout is 0.1s.
-    static const int64_t TEST_TIMEOUT = 100000000;
+    static constexpr int64_t TEST_TIMEOUT = 100000000;
 
     std::unique_ptr<PendingRequestPool> mPool;
 };
diff --git a/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h b/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h
index 0684655..9122955 100644
--- a/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h
@@ -18,9 +18,11 @@
 #define android_hardware_automotive_vehicle_aidl_impl_hardware_include_IVehicleHardware_H_
 
 #include <VehicleHalTypes.h>
+#include <VehicleUtils.h>
 
 #include <memory>
 #include <optional>
+#include <unordered_set>
 #include <vector>
 
 namespace android {
@@ -59,10 +61,23 @@
     using GetValuesCallback = std::function<void(std::vector<aidlvhal::GetValueResult>)>;
     using PropertyChangeCallback = std::function<void(std::vector<aidlvhal::VehiclePropValue>)>;
     using PropertySetErrorCallback = std::function<void(std::vector<SetValueErrorEvent>)>;
+    using SupportedValueChangeCallback = std::function<void(std::vector<PropIdAreaId>)>;
 
     virtual ~IVehicleHardware() = default;
 
     // Get all the property configs.
+    //
+    // Note that {@code VehicleAreaConfig.HasSupportedValueInfo} field is newly introduced in VHAL
+    // V4 to specify whether the [propertyId, areaId] has specified min/max supported value or
+    // supported values list.
+    //
+    // Since IVehicleHardware is designed to be backward compatible, this field can be set to null.
+    // If this field is set to null, VHAL client should fallback to use min/max supported value
+    // information in {@code VehicleAreaConfig} and {@code supportedEnumVaules} for enum properties.
+    //
+    // It is highly recommended to specify {@code VehicleAreaConfig.HasSupportedValueInfo} for new
+    // property implementations, even if the property does not specify supported values or the
+    // supported values are static.
     virtual std::vector<aidlvhal::VehiclePropConfig> getAllPropertyConfigs() const = 0;
 
     // Get the property configs for the specified propId. This is used for early-boot
@@ -240,6 +255,78 @@
                                                   [[maybe_unused]] float sampleRate) {
         return aidlvhal::StatusCode::OK;
     }
+
+    // Gets the min/max supported values for each of the specified [propId, areaId]s.
+    //
+    // The returned result may change dynamically.
+    //
+    // This is only called for [propId, areaId] that has
+    // {@code HasSupportedValueInfo.hasMinSupportedValue} or
+    // {@code HasSupportedValueInfo.hasMinSupportedValue} set to true.
+    //
+    // Client must implement (override) this function if at least one [propId, areaId]'s
+    // {@code HasSupportedValueInfo} is not null.
+    virtual std::vector<aidlvhal::MinMaxSupportedValueResult> getMinMaxSupportedValues(
+            [[maybe_unused]] const std::vector<PropIdAreaId>& propIdAreaIds) {
+        return {};
+    }
+
+    // Gets the supported values list for each of the specified [propId, areaId]s.
+    //
+    // The returned result may change dynamically.
+    //
+    // This is only called for [propId, areaId] that has
+    // {@code HasSupportedValueInfo.hasSupportedValuesList} set to true.
+    //
+    // Client must implement (override) this function if at least one [propId, areaId]'s
+    // {@code HasSupportedValueInfo} is not null.
+    virtual std::vector<aidlvhal::SupportedValuesListResult> getSupportedValuesLists(
+            [[maybe_unused]] const std::vector<PropIdAreaId>& propIdAreaIds) {
+        return {};
+    }
+
+    // Register a callback that would be called when the min/max supported value or supported
+    // values list change dynamically for propertyID returned from
+    // getPropertyIdsThatImplementGetSupportedValue
+    //
+    // This function must only be called once during initialization.
+    //
+    // Client must implement (override) this function if at least one [propId, areaId]'s
+    // {@code HasSupportedValueInfo} is not null.
+    virtual void registerSupportedValueChangeCallback(
+            [[maybe_unused]] std::unique_ptr<const SupportedValueChangeCallback> callback) {
+        // Do nothing.
+    }
+
+    // Subscribes to the min/max supported value or supported values list change for the specified
+    // [propId, areaId]s.
+    //
+    // If the propertyId's supported values are static, then must do nothing.
+    //
+    // If some of the [propId, areaId]s are already subscribed, then do nothing.
+    //
+    // This is only called for [propId, areaId] that has non-null {@code HasSupportedValueInfo}.
+    //
+    // Client must implement (override) this function if at least one [propId, areaId]'s
+    // {@code HasSupportedValueInfo} is not null.
+    virtual aidlvhal::StatusCode subscribeSupportedValueChange(
+            [[maybe_unused]] const std::vector<PropIdAreaId>& propIdAreaIds) {
+        return aidlvhal::StatusCode::OK;
+    }
+
+    // Unsubscrbies to the min/max supported value or supported values list change.
+    //
+    // Must do nothing if the [propId, areaId] was not previously subscribed to for supported
+    // values change.
+    //
+    // This is only called for [propId, areaId] that has non-null {@code HasSupportedValueInfo}.
+    //
+    // Client must implement (override) this function if at least one [propId, areaId]'s
+    // {@code HasSupportedValueInfo} is not null.
+    virtual aidlvhal::StatusCode unsubscribeSupportedValueChange(
+            [[maybe_unused]] const std::vector<PropIdAreaId>& propIdAreaIds) {
+        return aidlvhal::StatusCode::OK;
+    }
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/current/utils/common/include/VehicleHalTypes.h b/automotive/vehicle/aidl/impl/current/utils/common/include/VehicleHalTypes.h
index 4fa0a06..fcc006b 100644
--- a/automotive/vehicle/aidl/impl/current/utils/common/include/VehicleHalTypes.h
+++ b/automotive/vehicle/aidl/impl/current/utils/common/include/VehicleHalTypes.h
@@ -53,6 +53,8 @@
 #include <aidl/android/hardware/automotive/vehicle/LocationCharacterization.h>
 #include <aidl/android/hardware/automotive/vehicle/LowSpeedAutomaticEmergencyBrakingState.h>
 #include <aidl/android/hardware/automotive/vehicle/LowSpeedCollisionWarningState.h>
+#include <aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.h>
+#include <aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResults.h>
 #include <aidl/android/hardware/automotive/vehicle/Obd2CommonIgnitionMonitors.h>
 #include <aidl/android/hardware/automotive/vehicle/Obd2FuelSystemStatus.h>
 #include <aidl/android/hardware/automotive/vehicle/Obd2FuelType.h>
@@ -65,6 +67,8 @@
 #include <aidl/android/hardware/automotive/vehicle/SetValueResults.h>
 #include <aidl/android/hardware/automotive/vehicle/StatusCode.h>
 #include <aidl/android/hardware/automotive/vehicle/SubscribeOptions.h>
+#include <aidl/android/hardware/automotive/vehicle/SupportedValuesListResult.h>
+#include <aidl/android/hardware/automotive/vehicle/SupportedValuesListResults.h>
 #include <aidl/android/hardware/automotive/vehicle/VehicleAirbagLocation.h>
 #include <aidl/android/hardware/automotive/vehicle/VehicleApPowerBootupReason.h>
 #include <aidl/android/hardware/automotive/vehicle/VehicleApPowerStateReport.h>
diff --git a/automotive/vehicle/aidl/impl/current/utils/common/include/VehicleUtils.h b/automotive/vehicle/aidl/impl/current/utils/common/include/VehicleUtils.h
index 90a7c46..5b19100 100644
--- a/automotive/vehicle/aidl/impl/current/utils/common/include/VehicleUtils.h
+++ b/automotive/vehicle/aidl/impl/current/utils/common/include/VehicleUtils.h
@@ -310,6 +310,12 @@
     return toScopedAStatus(result, getErrorCode(result), additionalErrorMsg);
 }
 
+// This is for debug purpose only.
+inline std::string propIdToString(int32_t propId) {
+    return toString(
+            static_cast<aidl::android::hardware::automotive::vehicle::VehicleProperty>(propId));
+}
+
 struct PropIdAreaId {
     int32_t propId;
     int32_t areaId;
@@ -317,6 +323,11 @@
     inline bool operator==(const PropIdAreaId& other) const {
         return areaId == other.areaId && propId == other.propId;
     }
+
+    // This is for debug purpose only.
+    inline std::string toString() const {
+        return fmt::format("{{propId: {}, areaId: {}}}", propIdToString(propId), areaId);
+    }
 };
 
 struct PropIdAreaIdHash {
@@ -329,12 +340,6 @@
 };
 
 // This is for debug purpose only.
-inline std::string propIdToString(int32_t propId) {
-    return toString(
-            static_cast<aidl::android::hardware::automotive::vehicle::VehicleProperty>(propId));
-}
-
-// This is for debug purpose only.
 android::base::Result<int32_t> stringToPropId(const std::string& propName);
 
 // This is for debug purpose only. Converts an area's name to its enum definition.
@@ -362,4 +367,21 @@
 }  // namespace hardware
 }  // namespace android
 
+// Formatter must not be defined inside our namespace.
+template <>
+struct fmt::formatter<android::hardware::automotive::vehicle::PropIdAreaId> {
+    template <typename ParseContext>
+    constexpr auto parse(ParseContext& ctx) {
+        return ctx.begin();
+    }
+
+    template <typename FormatContext>
+    auto format(const android::hardware::automotive::vehicle::PropIdAreaId& p,
+                FormatContext& ctx) const {
+        return fmt::format_to(ctx.out(), "{{propId: {}, areaId: {}}}",
+                              android::hardware::automotive::vehicle::propIdToString(p.propId),
+                              p.areaId);
+    }
+};
+
 #endif  // android_hardware_automotive_vehicle_aidl_impl_utils_common_include_VehicleUtils_H_
diff --git a/automotive/vehicle/aidl/impl/current/utils/common/test/PendingRequestPoolTest.cpp b/automotive/vehicle/aidl/impl/current/utils/common/test/PendingRequestPoolTest.cpp
index 734c739..cc690d7 100644
--- a/automotive/vehicle/aidl/impl/current/utils/common/test/PendingRequestPoolTest.cpp
+++ b/automotive/vehicle/aidl/impl/current/utils/common/test/PendingRequestPoolTest.cpp
@@ -57,7 +57,7 @@
 
   private:
     // Test timeout is 0.1s.
-    static const int64_t TEST_TIMEOUT = 100000000;
+    static constexpr int64_t TEST_TIMEOUT = 100000000;
 
     std::unique_ptr<PendingRequestPool> mPool;
 };
diff --git a/automotive/vehicle/aidl/impl/current/utils/common/test/VehicleUtilsTest.cpp b/automotive/vehicle/aidl/impl/current/utils/common/test/VehicleUtilsTest.cpp
index 1048877..8278376 100644
--- a/automotive/vehicle/aidl/impl/current/utils/common/test/VehicleUtilsTest.cpp
+++ b/automotive/vehicle/aidl/impl/current/utils/common/test/VehicleUtilsTest.cpp
@@ -787,6 +787,29 @@
     ASSERT_FALSE(result.ok());
 }
 
+TEST(VehicleUtilsTest, testPropIdAreaIdToString) {
+    PropIdAreaId propIdAreaId = {
+            .propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED),
+            .areaId = 0,
+    };
+
+    ASSERT_EQ(propIdAreaId.toString(), "{propId: PERF_VEHICLE_SPEED, areaId: 0}");
+}
+
+TEST(VehicleUtilsTest, testPropIdAreaIdFormatter) {
+    PropIdAreaId propIdAreaId1 = {
+            .propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED),
+            .areaId = 0,
+    };
+    PropIdAreaId propIdAreaId2 = {
+            .propId = toInt(VehicleProperty::HVAC_FAN_SPEED),
+            .areaId = 1,
+    };
+
+    ASSERT_EQ(fmt::format("{}", std::vector<PropIdAreaId>{propIdAreaId1, propIdAreaId2}),
+              "[{propId: PERF_VEHICLE_SPEED, areaId: 0}, {propId: HVAC_FAN_SPEED, areaId: 1}]");
+}
+
 class InvalidPropValueTest : public testing::TestWithParam<InvalidPropValueTestCase> {};
 
 INSTANTIATE_TEST_SUITE_P(InvalidPropValueTests, InvalidPropValueTest,
diff --git a/automotive/vehicle/aidl/impl/current/vhal/include/DefaultVehicleHal.h b/automotive/vehicle/aidl/impl/current/vhal/include/DefaultVehicleHal.h
index 5d64e6f..02dbe9e 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/include/DefaultVehicleHal.h
+++ b/automotive/vehicle/aidl/impl/current/vhal/include/DefaultVehicleHal.h
@@ -233,6 +233,10 @@
             std::function<void(const std::unordered_map<int32_t, aidlvhal::VehiclePropConfig>&)>
                     callback) const EXCLUDES(mConfigLock);
 
+    android::base::Result<aidlvhal::VehicleAreaConfig> getAreaConfigForPropIdAreaId(
+            int32_t propId, int32_t areaId) const;
+    android::base::Result<aidlvhal::HasSupportedValueInfo> getHasSupportedValueInfo(
+            int32_t propId, int32_t areaId) const;
     // Puts the property change events into a queue so that they can handled in batch.
     static void batchPropertyChangeEvent(
             const std::weak_ptr<ConcurrentQueue<aidlvhal::VehiclePropValue>>& batchedEventQueue,
diff --git a/automotive/vehicle/aidl/impl/current/vhal/include/SubscriptionManager.h b/automotive/vehicle/aidl/impl/current/vhal/include/SubscriptionManager.h
index 2f16fca..59c21aa 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/include/SubscriptionManager.h
+++ b/automotive/vehicle/aidl/impl/current/vhal/include/SubscriptionManager.h
@@ -95,15 +95,14 @@
             bool isContinuousProperty);
 
     // Unsubscribes from the properties for the client.
-    // Returns error if the client was not subscribed before, or one of the given property was not
-    // subscribed, or one of the property failed to unsubscribe. Caller is safe to retry since
+    // Returns error if one of the property failed to unsubscribe. Caller is safe to retry since
     // unsubscribing to an already unsubscribed property is okay (it would be ignored).
     // Returns ok if all the requested properties for the client are unsubscribed.
     VhalResult<void> unsubscribe(ClientIdType client, const std::vector<int32_t>& propIds);
 
     // Unsubscribes from all the properties for the client.
-    // Returns error if the client was not subscribed before or one of the subscribed properties
-    // for the client failed to unsubscribe. Caller is safe to retry.
+    // Returns error one of the subscribed properties for the client failed to unsubscribe.
+    // Caller is safe to retry.
     // Returns ok if all the properties for the client are unsubscribed.
     VhalResult<void> unsubscribe(ClientIdType client);
 
@@ -119,8 +118,19 @@
                        std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropError>>
     getSubscribedClientsForErrorEvents(const std::vector<SetValueErrorEvent>& errorEvents);
 
-    // Returns the number of subscribed clients.
-    size_t countClients();
+    // Subscribes to supported values change.
+    VhalResult<void> subscribeSupportedValueChange(const CallbackType& callback,
+                                                   const std::vector<PropIdAreaId>& propIdAreaIds);
+
+    // Unsubscribes to supported values change.
+    VhalResult<void> unsubscribeSupportedValueChange(
+            ClientIdType client, const std::vector<PropIdAreaId>& propIdAreaIds);
+
+    // Returns the number of subscribed property change clients.
+    size_t countPropertyChangeClients();
+
+    // Returns the number of subscribed supported value change clients.
+    size_t countSupportedValueChangeClients();
 
     // Checks whether the sample rate is valid.
     static bool checkSampleRateHz(float sampleRateHz);
@@ -161,6 +171,11 @@
                        std::unordered_set<VehiclePropValue, VehiclePropValueHashPropIdAreaId,
                                           VehiclePropValueEqualPropIdAreaId>>
             mContSubValuesByCallback GUARDED_BY(mLock);
+    std::unordered_map<PropIdAreaId, std::unordered_map<ClientIdType, CallbackType>,
+                       PropIdAreaIdHash>
+            mSupportedValueChangeClientsByPropIdAreaId GUARDED_BY(mLock);
+    std::unordered_map<ClientIdType, std::unordered_set<PropIdAreaId, PropIdAreaIdHash>>
+            mSupportedValueChangePropIdAreaIdsByClient GUARDED_BY(mLock);
 
     VhalResult<void> addContinuousSubscriberLocked(const ClientIdType& clientId,
                                                    const PropIdAreaId& propIdAreaId,
@@ -181,6 +196,9 @@
     VhalResult<void> unsubscribePropIdAreaIdLocked(SubscriptionManager::ClientIdType clientId,
                                                    const PropIdAreaId& propIdAreaId)
             REQUIRES(mLock);
+    VhalResult<void> unsubscribeSupportedValueChangeLocked(
+            SubscriptionManager::ClientIdType clientId,
+            const std::vector<PropIdAreaId>& propIdAreaIds) REQUIRES(mLock);
 
     // Checks whether the manager is empty. For testing purpose.
     bool isEmpty();
diff --git a/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp
index 1e55a1a..25af50e 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp
@@ -49,7 +49,9 @@
 using ::aidl::android::hardware::automotive::vehicle::GetValueRequests;
 using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
 using ::aidl::android::hardware::automotive::vehicle::GetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::HasSupportedValueInfo;
 using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
+using ::aidl::android::hardware::automotive::vehicle::MinMaxSupportedValueResult;
 using ::aidl::android::hardware::automotive::vehicle::MinMaxSupportedValueResults;
 using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
 using ::aidl::android::hardware::automotive::vehicle::SetValueRequests;
@@ -57,6 +59,7 @@
 using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
 using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
+using ::aidl::android::hardware::automotive::vehicle::SupportedValuesListResult;
 using ::aidl::android::hardware::automotive::vehicle::SupportedValuesListResults;
 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
@@ -77,6 +80,8 @@
 using ::ndk::ScopedAStatus;
 using ::ndk::ScopedFileDescriptor;
 
+using VhalPropIdAreaId = ::aidl::android::hardware::automotive::vehicle::PropIdAreaId;
+
 std::string toString(const std::unordered_set<int64_t>& values) {
     std::string str = "";
     for (auto it = values.begin(); it != values.end(); it++) {
@@ -964,32 +969,223 @@
     return ScopedAStatus::ok();
 }
 
+Result<VehicleAreaConfig> DefaultVehicleHal::getAreaConfigForPropIdAreaId(int32_t propId,
+                                                                          int32_t areaId) const {
+    auto result = getConfig(propId);
+    if (!result.ok()) {
+        return Error() << "Failed to get property config for propertyId: " << propIdToString(propId)
+                       << ", error: " << result.error();
+    }
+    const VehiclePropConfig& config = result.value();
+    const VehicleAreaConfig* areaConfig = getAreaConfig(propId, areaId, config);
+    if (areaConfig == nullptr) {
+        return Error() << "AreaId config not found for propertyId: " << propIdToString(propId)
+                       << ", areaId: " << areaId;
+    }
+    return *areaConfig;
+}
+
+Result<HasSupportedValueInfo> DefaultVehicleHal::getHasSupportedValueInfo(int32_t propId,
+                                                                          int32_t areaId) const {
+    Result<VehicleAreaConfig> propIdAreaIdConfigResult =
+            getAreaConfigForPropIdAreaId(propId, areaId);
+    if (!isGlobalProp(propId) && !propIdAreaIdConfigResult.ok()) {
+        // For global property, it is possible that no config exists.
+        return Error() << propIdAreaIdConfigResult.error();
+    }
+    if (propIdAreaIdConfigResult.has_value()) {
+        auto areaConfig = propIdAreaIdConfigResult.value();
+        if (areaConfig.hasSupportedValueInfo.has_value()) {
+            return areaConfig.hasSupportedValueInfo.value();
+        }
+    }
+    return Error() << "property: " << propIdToString(propId) << ", areaId: " << areaId
+                   << " does not support this operation because hasSupportedValueInfo is null";
+}
+
 ScopedAStatus DefaultVehicleHal::getSupportedValuesLists(
-        const std::vector<::aidl::android::hardware::automotive::vehicle::PropIdAreaId>&,
-        SupportedValuesListResults*) {
-    // TODO(b/381020465): Add relevant implementation.
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+        const std::vector<VhalPropIdAreaId>& vhalPropIdAreaIds,
+        SupportedValuesListResults* supportedValuesListResults) {
+    std::vector<size_t> toHardwareRequestCounters;
+    std::vector<PropIdAreaId> toHardwarePropIdAreaIds;
+    std::vector<SupportedValuesListResult> results;
+    results.resize(vhalPropIdAreaIds.size());
+    for (size_t requestCounter = 0; requestCounter < vhalPropIdAreaIds.size(); requestCounter++) {
+        const auto& vhalPropIdAreaId = vhalPropIdAreaIds.at(requestCounter);
+        int32_t propId = vhalPropIdAreaId.propId;
+        int32_t areaId = vhalPropIdAreaId.areaId;
+        auto hasSupportedValueInfoResult = getHasSupportedValueInfo(propId, areaId);
+        if (!hasSupportedValueInfoResult.ok()) {
+            ALOGE("getSupportedValuesLists: %s",
+                  hasSupportedValueInfoResult.error().message().c_str());
+            results[requestCounter] = SupportedValuesListResult{
+                    .status = StatusCode::INVALID_ARG, .supportedValuesList = std::nullopt};
+            continue;
+        }
+
+        const auto& hasSupportedValueInfo = hasSupportedValueInfoResult.value();
+        if (hasSupportedValueInfo.hasSupportedValuesList) {
+            toHardwarePropIdAreaIds.push_back(PropIdAreaId{.propId = propId, .areaId = areaId});
+            toHardwareRequestCounters.push_back(requestCounter);
+        } else {
+            results[requestCounter] = SupportedValuesListResult{
+                    .status = StatusCode::OK, .supportedValuesList = std::nullopt};
+            continue;
+        }
+    }
+    if (toHardwarePropIdAreaIds.size() != 0) {
+        std::vector<SupportedValuesListResult> resultsFromHardware =
+                mVehicleHardware->getSupportedValuesLists(toHardwarePropIdAreaIds);
+        // It is guaranteed that toHardwarePropIdAreaIds, toHardwareRequestCounters,
+        // resultsFromHardware have the same size.
+        if (resultsFromHardware.size() != toHardwareRequestCounters.size()) {
+            return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                    toInt(StatusCode::INTERNAL_ERROR),
+                    fmt::format(
+                            "getSupportedValuesLists: Unexpected results size from IVehicleHardware"
+                            ", got: {}, expect: {}",
+                            resultsFromHardware.size(), toHardwareRequestCounters.size())
+                            .c_str());
+        }
+        for (size_t i = 0; i < toHardwareRequestCounters.size(); i++) {
+            results[toHardwareRequestCounters[i]] = resultsFromHardware[i];
+        }
+    }
+    ScopedAStatus status =
+            vectorToStableLargeParcelable(std::move(results), supportedValuesListResults);
+    if (!status.isOk()) {
+        int statusCode = status.getServiceSpecificError();
+        ALOGE("getSupportedValuesLists: failed to marshal result into large parcelable, error: "
+              "%s, code: %d",
+              status.getMessage(), statusCode);
+        return status;
+    }
+    return ScopedAStatus::ok();
 }
 
 ScopedAStatus DefaultVehicleHal::getMinMaxSupportedValue(
-        const std::vector<::aidl::android::hardware::automotive::vehicle::PropIdAreaId>&,
-        MinMaxSupportedValueResults*) {
-    // TODO(b/381020465): Add relevant implementation.
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+        const std::vector<VhalPropIdAreaId>& vhalPropIdAreaIds,
+        MinMaxSupportedValueResults* minMaxSupportedValueResults) {
+    std::vector<size_t> toHardwareRequestCounters;
+    std::vector<PropIdAreaId> toHardwarePropIdAreaIds;
+    std::vector<MinMaxSupportedValueResult> results;
+    results.resize(vhalPropIdAreaIds.size());
+    for (size_t requestCounter = 0; requestCounter < vhalPropIdAreaIds.size(); requestCounter++) {
+        const auto& vhalPropIdAreaId = vhalPropIdAreaIds.at(requestCounter);
+        int32_t propId = vhalPropIdAreaId.propId;
+        int32_t areaId = vhalPropIdAreaId.areaId;
+        auto hasSupportedValueInfoResult = getHasSupportedValueInfo(propId, areaId);
+        if (!hasSupportedValueInfoResult.ok()) {
+            ALOGE("getMinMaxSupportedValue: %s",
+                  hasSupportedValueInfoResult.error().message().c_str());
+            results[requestCounter] = MinMaxSupportedValueResult{.status = StatusCode::INVALID_ARG,
+                                                                 .minSupportedValue = std::nullopt,
+                                                                 .maxSupportedValue = std::nullopt};
+            continue;
+        }
+
+        const auto& hasSupportedValueInfo = hasSupportedValueInfoResult.value();
+        if (hasSupportedValueInfo.hasMinSupportedValue ||
+            hasSupportedValueInfo.hasMaxSupportedValue) {
+            toHardwarePropIdAreaIds.push_back(PropIdAreaId{.propId = propId, .areaId = areaId});
+            toHardwareRequestCounters.push_back(requestCounter);
+        } else {
+            results[requestCounter] = MinMaxSupportedValueResult{.status = StatusCode::OK,
+                                                                 .minSupportedValue = std::nullopt,
+                                                                 .maxSupportedValue = std::nullopt};
+            continue;
+        }
+    }
+    if (toHardwarePropIdAreaIds.size() != 0) {
+        std::vector<MinMaxSupportedValueResult> resultsFromHardware =
+                mVehicleHardware->getMinMaxSupportedValues(toHardwarePropIdAreaIds);
+        // It is guaranteed that toHardwarePropIdAreaIds, toHardwareRequestCounters,
+        // resultsFromHardware have the same size.
+        if (resultsFromHardware.size() != toHardwareRequestCounters.size()) {
+            return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                    toInt(StatusCode::INTERNAL_ERROR),
+                    fmt::format(
+                            "getMinMaxSupportedValue: Unexpected results size from IVehicleHardware"
+                            ", got: {}, expect: {}",
+                            resultsFromHardware.size(), toHardwareRequestCounters.size())
+                            .c_str());
+        }
+        for (size_t i = 0; i < toHardwareRequestCounters.size(); i++) {
+            results[toHardwareRequestCounters[i]] = resultsFromHardware[i];
+        }
+    }
+    ScopedAStatus status =
+            vectorToStableLargeParcelable(std::move(results), minMaxSupportedValueResults);
+    if (!status.isOk()) {
+        int statusCode = status.getServiceSpecificError();
+        ALOGE("getMinMaxSupportedValue: failed to marshal result into large parcelable, error: "
+              "%s, code: %d",
+              status.getMessage(), statusCode);
+        return status;
+    }
+    return ScopedAStatus::ok();
 }
 
 ScopedAStatus DefaultVehicleHal::registerSupportedValueChangeCallback(
-        const std::shared_ptr<IVehicleCallback>&,
-        const std::vector<::aidl::android::hardware::automotive::vehicle::PropIdAreaId>&) {
-    // TODO(b/381020465): Add relevant implementation.
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+        const std::shared_ptr<IVehicleCallback>& callback,
+        const std::vector<VhalPropIdAreaId>& vhalPropIdAreaIds) {
+    std::vector<PropIdAreaId> propIdAreaIdsToSubscribe;
+    for (size_t i = 0; i < vhalPropIdAreaIds.size(); i++) {
+        const auto& vhalPropIdAreaId = vhalPropIdAreaIds.at(i);
+        int32_t propId = vhalPropIdAreaId.propId;
+        int32_t areaId = vhalPropIdAreaId.areaId;
+        auto hasSupportedValueInfoResult = getHasSupportedValueInfo(propId, areaId);
+        if (!hasSupportedValueInfoResult.ok()) {
+            ALOGE("registerSupportedValueChangeCallback not supported: %s",
+                  hasSupportedValueInfoResult.error().message().c_str());
+            return toScopedAStatus(hasSupportedValueInfoResult, StatusCode::INVALID_ARG);
+        }
+        const auto& hasSupportedValueInfo = hasSupportedValueInfoResult.value();
+        if (!hasSupportedValueInfo.hasMinSupportedValue &&
+            !hasSupportedValueInfo.hasMaxSupportedValue &&
+            !hasSupportedValueInfo.hasSupportedValuesList) {
+            ALOGW("registerSupportedValueChangeCallback: do nothing for property: %s, "
+                  "areaId: %" PRId32
+                  ", no min/max supported values or supported values list"
+                  " specified",
+                  propIdToString(propId).c_str(), areaId);
+            continue;
+        }
+        propIdAreaIdsToSubscribe.push_back(PropIdAreaId{.propId = propId, .areaId = areaId});
+    }
+    if (propIdAreaIdsToSubscribe.empty()) {
+        return ScopedAStatus::ok();
+    }
+    auto result =
+            mSubscriptionManager->subscribeSupportedValueChange(callback, propIdAreaIdsToSubscribe);
+    if (!result.ok()) {
+        ALOGW("registerSupportedValueChangeCallback: failed to subscribe supported value change"
+              " for %s, error: %s",
+              fmt::format("{}", propIdAreaIdsToSubscribe).c_str(),
+              result.error().message().c_str());
+        return toScopedAStatus(result);
+    }
+    return ScopedAStatus::ok();
 }
 
 ScopedAStatus DefaultVehicleHal::unregisterSupportedValueChangeCallback(
-        const std::shared_ptr<IVehicleCallback>&,
-        const std::vector<::aidl::android::hardware::automotive::vehicle::PropIdAreaId>&) {
-    // TODO(b/381020465): Add relevant implementation.
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+        const std::shared_ptr<IVehicleCallback>& callback,
+        const std::vector<VhalPropIdAreaId>& vhalPropIdAreaIds) {
+    std::vector<PropIdAreaId> propIdAreaIds;
+    for (const auto& vhalPropIdAreaId : vhalPropIdAreaIds) {
+        propIdAreaIds.push_back(
+                PropIdAreaId{.propId = vhalPropIdAreaId.propId, .areaId = vhalPropIdAreaId.areaId});
+    }
+
+    auto result = mSubscriptionManager->unsubscribeSupportedValueChange(callback->asBinder().get(),
+                                                                        propIdAreaIds);
+    if (!result.ok()) {
+        ALOGW("unregisterSupportedValueChangeCallback: failed to unsubscribe supported value change"
+              " for %s, error: %s",
+              fmt::format("{}", propIdAreaIds).c_str(), result.error().message().c_str());
+        return toScopedAStatus(result);
+    }
+    return ScopedAStatus::ok();
 }
 
 IVehicleHardware* DefaultVehicleHal::getHardware() {
@@ -1107,12 +1303,14 @@
         dprintf(fd, "Currently have %zu getValues clients\n", mGetValuesClients.size());
         dprintf(fd, "Currently have %zu setValues clients\n", mSetValuesClients.size());
         dprintf(fd, "Currently have %zu subscribe clients\n", countSubscribeClients());
+        dprintf(fd, "Currently have %zu supported values change subscribe clients\n",
+                mSubscriptionManager->countSupportedValueChangeClients());
     }
     return STATUS_OK;
 }
 
 size_t DefaultVehicleHal::countSubscribeClients() {
-    return mSubscriptionManager->countClients();
+    return mSubscriptionManager->countPropertyChangeClients();
 }
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/current/vhal/src/SubscriptionManager.cpp b/automotive/vehicle/aidl/impl/current/vhal/src/SubscriptionManager.cpp
index 14ee707..f790033 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/src/SubscriptionManager.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/src/SubscriptionManager.cpp
@@ -17,6 +17,7 @@
 #include "SubscriptionManager.h"
 
 #include <VehicleUtils.h>
+#include <android-base/format.h>
 #include <android-base/stringprintf.h>
 #include <utils/Log.h>
 #include <utils/SystemClock.h>
@@ -65,6 +66,8 @@
 
     mClientsByPropIdAreaId.clear();
     mSubscribedPropsByClient.clear();
+    mSupportedValueChangeClientsByPropIdAreaId.clear();
+    mSupportedValueChangePropIdAreaIdsByClient.clear();
 }
 
 bool SubscriptionManager::checkSampleRateHz(float sampleRateHz) {
@@ -166,8 +169,7 @@
                                     /*enableVur*/ false));
         status != StatusCode::OK) {
         return StatusError(status)
-               << StringPrintf("failed subscribe for prop: %s, areaId: %" PRId32,
-                               propIdToString(propId).c_str(), areaId);
+               << fmt::format("failed subscribe for propIdAreaId: {}", propIdAreaId);
     }
     return {};
 }
@@ -345,8 +347,8 @@
     std::scoped_lock<std::mutex> lockGuard(mLock);
 
     if (mSubscribedPropsByClient.find(clientId) == mSubscribedPropsByClient.end()) {
-        return StatusError(StatusCode::INVALID_ARG)
-               << "No property was subscribed for the callback";
+        ALOGW("No property was subscribed for the callback, unsubscribe does nothing");
+        return {};
     }
 
     std::vector<PropIdAreaId> propIdAreaIdsToUnsubscribe;
@@ -378,16 +380,112 @@
     std::scoped_lock<std::mutex> lockGuard(mLock);
 
     if (mSubscribedPropsByClient.find(clientId) == mSubscribedPropsByClient.end()) {
-        return StatusError(StatusCode::INVALID_ARG) << "No property was subscribed for this client";
+        ALOGW("No property was subscribed for this client, unsubscribe does nothing");
+    } else {
+        auto& propIdAreaIds = mSubscribedPropsByClient[clientId];
+        for (auto const& propIdAreaId : propIdAreaIds) {
+            if (auto result = unsubscribePropIdAreaIdLocked(clientId, propIdAreaId); !result.ok()) {
+                return result;
+            }
+        }
+        mSubscribedPropsByClient.erase(clientId);
     }
 
-    auto& subscriptions = mSubscribedPropsByClient[clientId];
-    for (auto const& propIdAreaId : subscriptions) {
-        if (auto result = unsubscribePropIdAreaIdLocked(clientId, propIdAreaId); !result.ok()) {
+    if (mSupportedValueChangePropIdAreaIdsByClient.find(clientId) ==
+        mSupportedValueChangePropIdAreaIdsByClient.end()) {
+        ALOGW("No supported value change was subscribed for this client, unsubscribe does nothing");
+    } else {
+        const auto& propIdAreaIds = mSupportedValueChangePropIdAreaIdsByClient[clientId];
+        if (auto result = unsubscribeSupportedValueChangeLocked(
+                    clientId,
+                    std::vector<PropIdAreaId>(propIdAreaIds.begin(), propIdAreaIds.end()));
+            !result.ok()) {
             return result;
         }
     }
-    mSubscribedPropsByClient.erase(clientId);
+    return {};
+}
+
+VhalResult<void> SubscriptionManager::subscribeSupportedValueChange(
+        const std::shared_ptr<IVehicleCallback>& callback,
+        const std::vector<PropIdAreaId>& propIdAreaIds) {
+    // Need to make sure this whole operation is guarded by a lock so that our internal state is
+    // consistent with IVehicleHardware state.
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+
+    ClientIdType clientId = callback->asBinder().get();
+
+    // It is possible that some of the [propId, areaId]s are already subscribed, IVehicleHardware
+    // will ignore them.
+    if (auto status = mVehicleHardware->subscribeSupportedValueChange(propIdAreaIds);
+        status != StatusCode::OK) {
+        return StatusError(status)
+               << fmt::format("failed to call subscribeSupportedValueChange for propIdAreaIds: {}",
+                              propIdAreaIds);
+    }
+    for (const auto& propIdAreaId : propIdAreaIds) {
+        mSupportedValueChangeClientsByPropIdAreaId[propIdAreaId][clientId] = callback;
+        // mSupportedValueChangePropIdAreaIdsByClient[clientId] is a set so this will ignore
+        // duplicate [propId, areaId].
+        mSupportedValueChangePropIdAreaIdsByClient[clientId].insert(propIdAreaId);
+    }
+    return {};
+}
+
+VhalResult<void> SubscriptionManager::unsubscribeSupportedValueChange(
+        SubscriptionManager::ClientIdType clientId,
+        const std::vector<PropIdAreaId>& propIdAreaIds) {
+    // Need to make sure this whole operation is guarded by a lock so that our internal state is
+    // consistent with IVehicleHardware state.
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+
+    return unsubscribeSupportedValueChangeLocked(clientId, propIdAreaIds);
+}
+
+VhalResult<void> SubscriptionManager::unsubscribeSupportedValueChangeLocked(
+        SubscriptionManager::ClientIdType clientId,
+        const std::vector<PropIdAreaId>& propIdAreaIds) {
+    std::vector<PropIdAreaId> propIdAreaIdsToUnsubscribe;
+
+    // Check which [propId, areaId] needs to be unsubscribed from the hardware.
+    for (const auto& propIdAreaId : propIdAreaIds) {
+        auto it = mSupportedValueChangeClientsByPropIdAreaId.find(propIdAreaId);
+        if (it != mSupportedValueChangeClientsByPropIdAreaId.end()) {
+            const auto& clients = it->second;
+            if (clients.size() == 1 && clients.find(clientId) != clients.end()) {
+                // This callback is the only client registered for [propId, areaId].
+                // Unregister it should unregister the [propId, areaId].
+                propIdAreaIdsToUnsubscribe.push_back(propIdAreaId);
+            }
+        }
+    }
+
+    // Send the unsubscribe request.
+    if (!propIdAreaIdsToUnsubscribe.empty()) {
+        if (auto status =
+                    mVehicleHardware->unsubscribeSupportedValueChange(propIdAreaIdsToUnsubscribe);
+            status != StatusCode::OK) {
+            return StatusError(status) << fmt::format(
+                           "failed to call unsubscribeSupportedValueChange for "
+                           "propIdAreaIds: {}",
+                           propIdAreaIdsToUnsubscribe);
+        }
+    }
+
+    // Remove internal book-keeping.
+    for (const auto& propIdAreaId : propIdAreaIds) {
+        if (mSupportedValueChangeClientsByPropIdAreaId.find(propIdAreaId) !=
+            mSupportedValueChangeClientsByPropIdAreaId.end()) {
+            mSupportedValueChangeClientsByPropIdAreaId[propIdAreaId].erase(clientId);
+        }
+        if (mSupportedValueChangeClientsByPropIdAreaId.empty()) {
+            mSupportedValueChangeClientsByPropIdAreaId.erase(propIdAreaId);
+        }
+        mSupportedValueChangePropIdAreaIdsByClient[clientId].erase(propIdAreaId);
+        if (mSupportedValueChangePropIdAreaIdsByClient[clientId].empty()) {
+            mSupportedValueChangePropIdAreaIdsByClient.erase(clientId);
+        }
+    }
     return {};
 }
 
@@ -485,14 +583,21 @@
 
 bool SubscriptionManager::isEmpty() {
     std::scoped_lock<std::mutex> lockGuard(mLock);
-    return mSubscribedPropsByClient.empty() && mClientsByPropIdAreaId.empty();
+    return mSubscribedPropsByClient.empty() && mClientsByPropIdAreaId.empty() &&
+           mSupportedValueChangeClientsByPropIdAreaId.empty() &&
+           mSupportedValueChangePropIdAreaIdsByClient.empty();
 }
 
-size_t SubscriptionManager::countClients() {
+size_t SubscriptionManager::countPropertyChangeClients() {
     std::scoped_lock<std::mutex> lockGuard(mLock);
     return mSubscribedPropsByClient.size();
 }
 
+size_t SubscriptionManager::countSupportedValueChangeClients() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return mSupportedValueChangePropIdAreaIdsByClient.size();
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp
index ad34a4c..0526f7d 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp
@@ -21,6 +21,7 @@
 
 #include <IVehicleHardware.h>
 #include <LargeParcelableBase.h>
+#include <aidl/android/hardware/automotive/vehicle/HasSupportedValueInfo.h>
 #include <aidl/android/hardware/automotive/vehicle/IVehicle.h>
 #include <aidl/android/hardware/automotive/vehicle/IVehicleCallback.h>
 
@@ -51,14 +52,20 @@
 using ::aidl::android::hardware::automotive::vehicle::GetValueRequests;
 using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
 using ::aidl::android::hardware::automotive::vehicle::GetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::HasSupportedValueInfo;
 using ::aidl::android::hardware::automotive::vehicle::IVehicle;
 using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
+using ::aidl::android::hardware::automotive::vehicle::MinMaxSupportedValueResult;
+using ::aidl::android::hardware::automotive::vehicle::MinMaxSupportedValueResults;
+using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
 using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
 using ::aidl::android::hardware::automotive::vehicle::SetValueRequests;
 using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
 using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
 using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
+using ::aidl::android::hardware::automotive::vehicle::SupportedValuesListResult;
+using ::aidl::android::hardware::automotive::vehicle::SupportedValuesListResults;
 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWindow;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
@@ -85,6 +92,8 @@
 using ::testing::UnorderedElementsAreArray;
 using ::testing::WhenSortedBy;
 
+using VhalPropIdAreaId = ::aidl::android::hardware::automotive::vehicle::PropIdAreaId;
+
 constexpr int32_t INVALID_PROP_ID = 0;
 // VehiclePropertyGroup:VENDOR,VehicleArea:WINDOW,VehiclePropertyType:INT32
 constexpr int32_t INT32_WINDOW_PROP = 10001 + 0x20000000 + 0x03000000 + 0x00400000;
@@ -112,6 +121,11 @@
     return static_cast<int32_t>(i) + 0x20000000 + 0x01000000 + 0x00410000;
 }
 
+int32_t testInt32VecWindowProp(size_t i) {
+    // VehiclePropertyGroup:VENDOR,VehicleArea:WINDOW,VehiclePropertyType:INT32_VEC
+    return static_cast<int32_t>(i) + 0x20000000 + 0x03000000 + 0x00410000;
+}
+
 std::string toString(const std::vector<SubscribeOptions>& options) {
     std::string optionsStr;
     for (const auto& option : options) {
@@ -1828,12 +1842,11 @@
     ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::ACCESS_DENIED));
 }
 
-TEST_F(DefaultVehicleHalTest, testUnsubscribeFailure) {
+TEST_F(DefaultVehicleHalTest, testUnsubscribeNotSubscribedProperty) {
     auto status = getClient()->unsubscribe(getCallbackClient(),
                                            std::vector<int32_t>({GLOBAL_ON_CHANGE_PROP}));
 
-    ASSERT_FALSE(status.isOk()) << "unsubscribe to a not-subscribed property must fail";
-    ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::INVALID_ARG));
+    ASSERT_TRUE(status.isOk()) << "unsubscribe to a not-subscribed property must do nothing";
 }
 
 TEST_F(DefaultVehicleHalTest, testHeartbeatEvent) {
@@ -2121,6 +2134,489 @@
     }
 }
 
+TEST_F(DefaultVehicleHalTest, testGetSupportedValuesLists) {
+    auto testConfigs = std::vector<VehiclePropConfig>(
+            {// This ia valid request, but no supported values are specified.
+             VehiclePropConfig{
+                     .prop = testInt32VecProp(1),
+                     .areaConfigs =
+                             {
+                                     {.areaId = 0,
+                                      .hasSupportedValueInfo =
+                                              HasSupportedValueInfo{
+                                                      .hasSupportedValuesList = false,
+                                              }},
+                             },
+             },
+             // This is an invalid request since hasSupportedValueInfo is null. This is not
+             // supported.
+             VehiclePropConfig{
+                     .prop = testInt32VecWindowProp(2),
+                     .areaConfigs =
+                             {
+                                     {
+                                             .areaId = 2,
+                                     },
+                             },
+             },
+             // This is an invalid request for global property.
+             VehiclePropConfig{
+                     .prop = testInt32VecProp(3),
+             },
+             // This is a normal request.
+             VehiclePropConfig{
+                     .prop = testInt32VecWindowProp(4),
+                     .areaConfigs =
+                             {
+                                     {.areaId = 4,
+                                      .hasSupportedValueInfo =
+                                              HasSupportedValueInfo{
+                                                      .hasSupportedValuesList = true,
+                                              }},
+                             },
+             }});
+
+    auto hardware = std::make_unique<MockVehicleHardware>();
+    MockVehicleHardware* hardwarePtr = hardware.get();
+    hardware->setPropertyConfigs(testConfigs);
+
+    SupportedValuesListResult resultFromHardware = {
+            .status = StatusCode::OK,
+            .supportedValuesList =
+                    std::vector<std::optional<RawPropValues>>{RawPropValues{.int32Values = {1}}}};
+    auto response = std::vector<SupportedValuesListResult>({resultFromHardware});
+    hardware->setSupportedValuesListResponse(response);
+
+    auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+    std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+    SupportedValuesListResults results;
+
+    auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecProp(1), .areaId = 0};
+    auto propIdAreaId2 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2};
+    auto propIdAreaId3 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(3), .areaId = 0};
+    auto propIdAreaId4 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(4), .areaId = 4};
+    auto status = vhal->getSupportedValuesLists(
+            std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2, propIdAreaId3,
+                                          propIdAreaId4},
+            &results);
+
+    ASSERT_TRUE(status.isOk()) << "Get non-okay status from getSupportedValuesLists"
+                               << status.getMessage();
+    ASSERT_THAT(hardwarePtr->getSupportedValuesListRequest(),
+                ElementsAre(PropIdAreaId{.propId = testInt32VecWindowProp(4), .areaId = 4}))
+            << "Only valid request 4 should get to hardware";
+
+    ASSERT_EQ(results.payloads.size(), 4u);
+    SupportedValuesListResult result = results.payloads[0];
+    ASSERT_EQ(result.status, StatusCode::OK)
+            << "Must return OK even if the supported values list is not specified";
+    ASSERT_FALSE(result.supportedValuesList.has_value())
+            << "Must return an empty supported values list if not specified";
+
+    result = results.payloads[1];
+    ASSERT_EQ(result.status, StatusCode::INVALID_ARG)
+            << "PropId, areaId that set hasSupportedValueInfo to null must not be supported";
+    ASSERT_FALSE(result.supportedValuesList.has_value());
+
+    result = results.payloads[2];
+    ASSERT_EQ(result.status, StatusCode::INVALID_ARG)
+            << "Must return INVALID_ARG for global property without area config";
+    ASSERT_FALSE(result.supportedValuesList.has_value());
+
+    result = results.payloads[3];
+    ASSERT_EQ(result.status, StatusCode::OK);
+    ASSERT_TRUE(result.supportedValuesList.has_value());
+    ASSERT_EQ(result.supportedValuesList.value().size(), 1u);
+    ASSERT_EQ(result.supportedValuesList.value()[0]->int32Values.size(), 1u);
+    ASSERT_EQ((result.supportedValuesList.value())[0]->int32Values[0], 1);
+}
+
+TEST_F(DefaultVehicleHalTest, testGetSupportedValuesLists_propIdAreaIdNotFound) {
+    auto testConfigs = std::vector<VehiclePropConfig>({
+            VehiclePropConfig{
+                    .prop = testInt32VecWindowProp(1),
+                    .areaConfigs =
+                            {
+                                    {.areaId = 1,
+                                     .hasSupportedValueInfo =
+                                             HasSupportedValueInfo{
+                                                     .hasSupportedValuesList = true,
+                                             }},
+                            },
+            },
+    });
+
+    auto hardware = std::make_unique<MockVehicleHardware>();
+    hardware->setPropertyConfigs(testConfigs);
+
+    auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+    std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+    SupportedValuesListResults results;
+
+    // propId not valid.
+    auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 1};
+    // areaId not valid.
+    auto propIdAreaId2 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(1), .areaId = 2};
+
+    auto status = vhal->getSupportedValuesLists(
+            std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2}, &results);
+
+    ASSERT_TRUE(status.isOk()) << "Get non-okay status from getSupportedValuesLists"
+                               << status.getMessage();
+    ASSERT_EQ(results.payloads.size(), 2u);
+    SupportedValuesListResult result = results.payloads[0];
+    ASSERT_EQ(result.status, StatusCode::INVALID_ARG);
+    result = results.payloads[1];
+    ASSERT_EQ(result.status, StatusCode::INVALID_ARG);
+}
+
+TEST_F(DefaultVehicleHalTest, testGetMinMaxSupportedValue) {
+    auto testConfigs = std::vector<VehiclePropConfig>(
+            {// This ia valid request, but no supported values are specified.
+             VehiclePropConfig{
+                     .prop = testInt32VecProp(1),
+                     .areaConfigs =
+                             {
+                                     {.areaId = 0,
+                                      .hasSupportedValueInfo =
+                                              HasSupportedValueInfo{
+                                                      .hasMinSupportedValue = false,
+                                                      .hasMaxSupportedValue = false,
+                                              }},
+                             },
+             },
+             // This is an invalid request since hasSupportedValueInfo is null. This is not
+             // supported.
+             VehiclePropConfig{
+                     .prop = testInt32VecWindowProp(2),
+                     .areaConfigs =
+                             {
+                                     {
+                                             .areaId = 2,
+                                     },
+                             },
+             },
+             // This is an invalid request for global property.
+             VehiclePropConfig{
+                     .prop = testInt32VecProp(3),
+             },
+             // This is a normal request.
+             VehiclePropConfig{
+                     .prop = testInt32VecWindowProp(4),
+                     .areaConfigs =
+                             {
+                                     {.areaId = 4,
+                                      .hasSupportedValueInfo =
+                                              HasSupportedValueInfo{
+                                                      .hasMinSupportedValue = true,
+                                                      .hasMaxSupportedValue = false,
+                                              }},
+                             },
+             }});
+
+    auto hardware = std::make_unique<MockVehicleHardware>();
+    MockVehicleHardware* hardwarePtr = hardware.get();
+    hardware->setPropertyConfigs(testConfigs);
+
+    MinMaxSupportedValueResult resultFromHardware = {
+            .status = StatusCode::OK,
+            .minSupportedValue = std::optional<RawPropValues>{RawPropValues{.int32Values = {1}}},
+            .maxSupportedValue = std::nullopt,
+    };
+    auto response = std::vector<MinMaxSupportedValueResult>({resultFromHardware});
+    hardware->setMinMaxSupportedValueResponse(response);
+
+    auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+    std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+    MinMaxSupportedValueResults results;
+
+    auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecProp(1), .areaId = 0};
+    auto propIdAreaId2 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2};
+    auto propIdAreaId3 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(3), .areaId = 0};
+    auto propIdAreaId4 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(4), .areaId = 4};
+    auto status = vhal->getMinMaxSupportedValue(
+            std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2, propIdAreaId3,
+                                          propIdAreaId4},
+            &results);
+
+    ASSERT_TRUE(status.isOk()) << "Get non-okay status from getMinMaxSupportedValue"
+                               << status.getMessage();
+    ASSERT_THAT(hardwarePtr->getMinMaxSupportedValueRequest(),
+                ElementsAre(PropIdAreaId{.propId = testInt32VecWindowProp(4), .areaId = 4}))
+            << "Only valid request 4 should get to hardware";
+
+    ASSERT_EQ(results.payloads.size(), 4u);
+    MinMaxSupportedValueResult result = results.payloads[0];
+    ASSERT_EQ(result.status, StatusCode::OK)
+            << "Must return OK even if the min/max supported values are not specified";
+    ASSERT_FALSE(result.minSupportedValue.has_value())
+            << "Must return null min supported value if not specified";
+    ASSERT_FALSE(result.maxSupportedValue.has_value())
+            << "Must return null max supported value if not specified";
+
+    result = results.payloads[1];
+    ASSERT_EQ(result.status, StatusCode::INVALID_ARG)
+            << "PropId, areaId that set hasSupportedValueInfo to null must not be supported";
+    ASSERT_FALSE(result.minSupportedValue.has_value());
+    ASSERT_FALSE(result.maxSupportedValue.has_value());
+
+    result = results.payloads[2];
+    ASSERT_EQ(result.status, StatusCode::INVALID_ARG)
+            << "Must return INVALID_ARG for global property without area config";
+    ASSERT_FALSE(result.minSupportedValue.has_value());
+    ASSERT_FALSE(result.maxSupportedValue.has_value());
+
+    result = results.payloads[3];
+    ASSERT_EQ(result.status, StatusCode::OK);
+    ASSERT_TRUE(result.minSupportedValue.has_value());
+    ASSERT_EQ(result.minSupportedValue->int32Values.size(), 1u);
+    ASSERT_EQ(result.minSupportedValue->int32Values[0], 1);
+    ASSERT_FALSE(result.maxSupportedValue.has_value());
+}
+
+TEST_F(DefaultVehicleHalTest, testGetMinMaxSupportedValue_propIdAreaIdNotFound) {
+    auto testConfigs = std::vector<VehiclePropConfig>({
+            VehiclePropConfig{
+                    .prop = testInt32VecWindowProp(1),
+                    .areaConfigs =
+                            {
+                                    {.areaId = 1,
+                                     .hasSupportedValueInfo =
+                                             HasSupportedValueInfo{
+                                                     .hasMinSupportedValue = true,
+                                                     .hasMaxSupportedValue = true,
+                                             }},
+                            },
+            },
+    });
+
+    auto hardware = std::make_unique<MockVehicleHardware>();
+    hardware->setPropertyConfigs(testConfigs);
+
+    auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+    std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+    MinMaxSupportedValueResults results;
+
+    // propId not valid.
+    auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 1};
+    // areaId not valid.
+    auto propIdAreaId2 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(1), .areaId = 2};
+
+    auto status = vhal->getMinMaxSupportedValue(
+            std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2}, &results);
+
+    ASSERT_TRUE(status.isOk()) << "Get non-okay status from getMinMaxSupportedValue"
+                               << status.getMessage();
+    ASSERT_EQ(results.payloads.size(), 2u);
+    MinMaxSupportedValueResult result = results.payloads[0];
+    ASSERT_EQ(result.status, StatusCode::INVALID_ARG);
+    result = results.payloads[1];
+    ASSERT_EQ(result.status, StatusCode::INVALID_ARG);
+}
+
+TEST_F(DefaultVehicleHalTest, testRegisterSupportedValueChangeCallback) {
+    auto testConfigs = std::vector<VehiclePropConfig>(
+            {VehiclePropConfig{
+                     .prop = testInt32VecProp(1),
+                     .areaConfigs =
+                             {
+                                     {.areaId = 0,
+                                      .hasSupportedValueInfo =
+                                              HasSupportedValueInfo{
+                                                      .hasMinSupportedValue = false,
+                                                      .hasMaxSupportedValue = false,
+                                                      .hasSupportedValuesList = false,
+                                              }},
+                             },
+             },
+             VehiclePropConfig{
+                     .prop = testInt32VecWindowProp(2),
+                     .areaConfigs =
+                             {
+                                     {.areaId = 2,
+                                      .hasSupportedValueInfo =
+                                              HasSupportedValueInfo{
+                                                      .hasMinSupportedValue = true,
+                                                      .hasMaxSupportedValue = false,
+                                                      .hasSupportedValuesList = false,
+                                              }},
+                             },
+             }});
+
+    auto hardware = std::make_unique<MockVehicleHardware>();
+    MockVehicleHardware* hardwarePtr = hardware.get();
+    hardware->setPropertyConfigs(testConfigs);
+
+    auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+    std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+    // This request is ignored because it does not have supported value info.
+    auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecProp(1), .areaId = 0};
+    auto propIdAreaId2 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2};
+    auto status = client->registerSupportedValueChangeCallback(
+            getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2});
+
+    ASSERT_TRUE(status.isOk()) << "Get non-okay status from registerSupportedValueChangeCallback"
+                               << status.getMessage();
+    ASSERT_THAT(hardwarePtr->getSubscribedSupportedValueChangePropIdAreaIds(),
+                ElementsAre(PropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2}));
+}
+
+TEST_F(DefaultVehicleHalTest, testRegisterSupportedValueChangeCallback_invalidRequest) {
+    auto testConfigs = std::vector<VehiclePropConfig>({VehiclePropConfig{
+            .prop = testInt32VecProp(1),
+            .areaConfigs =
+                    {
+                            {.areaId = 0, .hasSupportedValueInfo = std::nullopt},
+                    },
+    }});
+    auto hardware = std::make_unique<MockVehicleHardware>();
+    hardware->setPropertyConfigs(testConfigs);
+
+    auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+    std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+    auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecProp(1), .areaId = 0};
+    auto status = client->registerSupportedValueChangeCallback(
+            getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId1});
+
+    ASSERT_FALSE(status.isOk()) << "registerSupportedValueChangeCallback must return error if one "
+                                   "of the requested [propId, areaId]"
+                                   " does not have supportedValueInfo";
+}
+
+TEST_F(DefaultVehicleHalTest, testRegisterSupportedValueChangeCallback_errorStatusFromHardware) {
+    auto testConfigs = std::vector<VehiclePropConfig>({VehiclePropConfig{
+            .prop = testInt32VecWindowProp(2),
+            .areaConfigs =
+                    {
+                            {.areaId = 2,
+                             .hasSupportedValueInfo =
+                                     HasSupportedValueInfo{
+                                             .hasMinSupportedValue = true,
+                                             .hasMaxSupportedValue = false,
+                                             .hasSupportedValuesList = false,
+                                     }},
+                    },
+    }});
+
+    auto hardware = std::make_unique<MockVehicleHardware>();
+    hardware->setStatus("subscribeSupportedValueChange", StatusCode::INTERNAL_ERROR);
+    hardware->setPropertyConfigs(testConfigs);
+
+    auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+    std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+    auto propIdAreaId = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2};
+    auto status = client->registerSupportedValueChangeCallback(
+            getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId});
+
+    ASSERT_FALSE(status.isOk()) << "registerSupportedValueChangeCallback must return error if "
+                                   "VehicleHardware returns error";
+}
+
+TEST_F(DefaultVehicleHalTest, testUnregisterSupportedValueChangeCallback) {
+    auto testConfigs = std::vector<VehiclePropConfig>(
+            {VehiclePropConfig{
+                     .prop = testInt32VecProp(1),
+                     .areaConfigs =
+                             {
+                                     {.areaId = 0,
+                                      .hasSupportedValueInfo =
+                                              HasSupportedValueInfo{
+                                                      .hasMinSupportedValue = false,
+                                                      .hasMaxSupportedValue = false,
+                                                      .hasSupportedValuesList = true,
+                                              }},
+                             },
+             },
+             VehiclePropConfig{
+                     .prop = testInt32VecWindowProp(2),
+                     .areaConfigs =
+                             {
+                                     {.areaId = 2,
+                                      .hasSupportedValueInfo =
+                                              HasSupportedValueInfo{
+                                                      .hasMinSupportedValue = true,
+                                                      .hasMaxSupportedValue = false,
+                                                      .hasSupportedValuesList = false,
+                                              }},
+                             },
+             }});
+
+    auto hardware = std::make_unique<MockVehicleHardware>();
+    MockVehicleHardware* hardwarePtr = hardware.get();
+    hardware->setPropertyConfigs(testConfigs);
+
+    auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+    std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+    auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecProp(1), .areaId = 0};
+    auto propIdAreaId2 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2};
+    auto status = client->registerSupportedValueChangeCallback(
+            getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2});
+
+    ASSERT_TRUE(status.isOk()) << "Get non-okay status from registerSupportedValueChangeCallback"
+                               << status.getMessage();
+
+    status = client->unregisterSupportedValueChangeCallback(
+            getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2});
+
+    ASSERT_TRUE(status.isOk()) << "Get non-okay status from unregisterSupportedValueChangeCallback"
+                               << status.getMessage();
+
+    ASSERT_TRUE(hardwarePtr->getSubscribedSupportedValueChangePropIdAreaIds().empty())
+            << "All registered [propId, areaId]s must be unregistered";
+}
+
+TEST_F(DefaultVehicleHalTest, testUnregisterSupportedValueChangeCallback_ignoreUnregistered) {
+    auto testConfigs = std::vector<VehiclePropConfig>(
+            {VehiclePropConfig{
+                     .prop = testInt32VecProp(1),
+                     .areaConfigs =
+                             {
+                                     {.areaId = 0,
+                                      .hasSupportedValueInfo =
+                                              HasSupportedValueInfo{
+                                                      .hasMinSupportedValue = false,
+                                                      .hasMaxSupportedValue = false,
+                                                      .hasSupportedValuesList = true,
+                                              }},
+                             },
+             },
+             VehiclePropConfig{
+                     .prop = testInt32VecWindowProp(2),
+                     .areaConfigs =
+                             {
+                                     {.areaId = 2,
+                                      .hasSupportedValueInfo =
+                                              HasSupportedValueInfo{
+                                                      .hasMinSupportedValue = true,
+                                                      .hasMaxSupportedValue = false,
+                                                      .hasSupportedValuesList = false,
+                                              }},
+                             },
+             }});
+
+    auto hardware = std::make_unique<MockVehicleHardware>();
+    MockVehicleHardware* hardwarePtr = hardware.get();
+    hardware->setPropertyConfigs(testConfigs);
+
+    auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+    std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+    auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecProp(1), .areaId = 0};
+    auto propIdAreaId2 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2};
+    auto status = client->unregisterSupportedValueChangeCallback(
+            getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2});
+
+    ASSERT_TRUE(status.isOk());
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp
index e796ce5..11f1835 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp
@@ -26,10 +26,12 @@
 
 using ::aidl::android::hardware::automotive::vehicle::GetValueRequest;
 using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::MinMaxSupportedValueResult;
 using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
 using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
 using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
+using ::aidl::android::hardware::automotive::vehicle::SupportedValuesListResult;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 
@@ -200,6 +202,20 @@
     return propIdAreaIds;
 }
 
+std::vector<SupportedValuesListResult> MockVehicleHardware::getSupportedValuesLists(
+        const std::vector<PropIdAreaId>& propIdAreaIds) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mSupportedValuesListRequest = propIdAreaIds;
+    return mSupportedValuesListResponse;
+}
+
+std::vector<MinMaxSupportedValueResult> MockVehicleHardware::getMinMaxSupportedValues(
+        const std::vector<PropIdAreaId>& propIdAreaIds) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mMinMaxSupportedValueRequest = propIdAreaIds;
+    return mMinMaxSupportedValueResponse;
+}
+
 void MockVehicleHardware::registerOnPropertyChangeEvent(
         std::unique_ptr<const PropertyChangeCallback> callback) {
     std::scoped_lock<std::mutex> lockGuard(mLock);
@@ -267,11 +283,67 @@
     mEventBatchingWindow = window;
 }
 
+void MockVehicleHardware::setSupportedValuesListResponse(
+        const std::vector<SupportedValuesListResult>& response) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mSupportedValuesListResponse = response;
+}
+
+void MockVehicleHardware::setMinMaxSupportedValueResponse(
+        const std::vector<MinMaxSupportedValueResult>& response) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mMinMaxSupportedValueResponse = response;
+}
+
+std::vector<PropIdAreaId> MockVehicleHardware::getSupportedValuesListRequest() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return mSupportedValuesListRequest;
+}
+
+std::vector<PropIdAreaId> MockVehicleHardware::getMinMaxSupportedValueRequest() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return mMinMaxSupportedValueRequest;
+}
+
 std::chrono::nanoseconds MockVehicleHardware::getPropertyOnChangeEventBatchingWindow() {
     std::scoped_lock<std::mutex> lockGuard(mLock);
     return mEventBatchingWindow;
 }
 
+StatusCode MockVehicleHardware::subscribeSupportedValueChange(
+        const std::vector<PropIdAreaId>& propIdAreaIds) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    if (auto it = mStatusByFunctions.find(__func__); it != mStatusByFunctions.end()) {
+        if (StatusCode status = it->second; status != StatusCode::OK) {
+            return status;
+        }
+    }
+    for (const auto& propIdAreaId : propIdAreaIds) {
+        mSubscribedSupportedValueChangePropIdAreaIds.insert(propIdAreaId);
+    }
+    return StatusCode::OK;
+}
+
+StatusCode MockVehicleHardware::unsubscribeSupportedValueChange(
+        const std::vector<PropIdAreaId>& propIdAreaIds) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    if (auto it = mStatusByFunctions.find(__func__); it != mStatusByFunctions.end()) {
+        if (StatusCode status = it->second; status != StatusCode::OK) {
+            return status;
+        }
+    }
+    for (const auto& propIdAreaId : propIdAreaIds) {
+        mSubscribedSupportedValueChangePropIdAreaIds.erase(propIdAreaId);
+    }
+    return StatusCode::OK;
+}
+
+std::unordered_set<PropIdAreaId, PropIdAreaIdHash>
+MockVehicleHardware::getSubscribedSupportedValueChangePropIdAreaIds() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return mSubscribedSupportedValueChangePropIdAreaIds;
+}
+
 template <class ResultType>
 StatusCode MockVehicleHardware::returnResponse(
         std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
diff --git a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h
index 06e01a8..e7359db 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h
@@ -67,6 +67,14 @@
     aidl::android::hardware::automotive::vehicle::StatusCode unsubscribe(int32_t propId,
                                                                          int32_t areaId) override;
     std::chrono::nanoseconds getPropertyOnChangeEventBatchingWindow() override;
+    std::vector<aidl::android::hardware::automotive::vehicle::SupportedValuesListResult>
+    getSupportedValuesLists(const std::vector<PropIdAreaId>& propIdAreaIds) override;
+    std::vector<aidl::android::hardware::automotive::vehicle::MinMaxSupportedValueResult>
+    getMinMaxSupportedValues(const std::vector<PropIdAreaId>& propIdAreaIds) override;
+    aidl::android::hardware::automotive::vehicle::StatusCode subscribeSupportedValueChange(
+            const std::vector<PropIdAreaId>& propIdAreaIds) override;
+    aidl::android::hardware::automotive::vehicle::StatusCode unsubscribeSupportedValueChange(
+            const std::vector<PropIdAreaId>& propIdAreaIds) override;
 
     // Test functions.
     void setPropertyConfigs(
@@ -78,6 +86,14 @@
     void addSetValueResponses(
             const std::vector<aidl::android::hardware::automotive::vehicle::SetValueResult>&
                     responses);
+    void setSupportedValuesListResponse(
+            const std::vector<
+                    aidl::android::hardware::automotive::vehicle::SupportedValuesListResult>&
+                    response);
+    void setMinMaxSupportedValueResponse(
+            const std::vector<
+                    aidl::android::hardware::automotive::vehicle::MinMaxSupportedValueResult>&
+                    response);
     void setGetValueResponder(
             std::function<aidl::android::hardware::automotive::vehicle::StatusCode(
                     std::shared_ptr<const GetValuesCallback>,
@@ -88,6 +104,8 @@
     nextGetValueRequests();
     std::vector<aidl::android::hardware::automotive::vehicle::SetValueRequest>
     nextSetValueRequests();
+    std::vector<PropIdAreaId> getSupportedValuesListRequest();
+    std::vector<PropIdAreaId> getMinMaxSupportedValueRequest();
     void setStatus(const char* functionName,
                    aidl::android::hardware::automotive::vehicle::StatusCode status);
     void setSleepTime(int64_t timeInNano);
@@ -100,10 +118,13 @@
     std::vector<aidl::android::hardware::automotive::vehicle::SubscribeOptions>
     getSubscribeOptions();
     void clearSubscribeOptions();
-    // Whether getAllPropertyConfigs() has been called, which blocks all all property configs
+    // Whether getAllPropertyConfigs() has been called, which blocks on all property configs
     // being ready.
     bool getAllPropertyConfigsCalled();
 
+    std::unordered_set<PropIdAreaId, PropIdAreaIdHash>
+    getSubscribedSupportedValueChangePropIdAreaIds();
+
   private:
     mutable std::mutex mLock;
     mutable std::condition_variable mCv;
@@ -118,6 +139,12 @@
             mSetValueRequests GUARDED_BY(mLock);
     mutable std::list<std::vector<aidl::android::hardware::automotive::vehicle::SetValueResult>>
             mSetValueResponses GUARDED_BY(mLock);
+    mutable std::vector<PropIdAreaId> mSupportedValuesListRequest GUARDED_BY(mLock);
+    mutable std::vector<PropIdAreaId> mMinMaxSupportedValueRequest GUARDED_BY(mLock);
+    mutable std::vector<aidl::android::hardware::automotive::vehicle::SupportedValuesListResult>
+            mSupportedValuesListResponse GUARDED_BY(mLock);
+    mutable std::vector<aidl::android::hardware::automotive::vehicle::MinMaxSupportedValueResult>
+            mMinMaxSupportedValueResponse GUARDED_BY(mLock);
     std::unordered_map<const char*, aidl::android::hardware::automotive::vehicle::StatusCode>
             mStatusByFunctions GUARDED_BY(mLock);
     int64_t mSleepTime GUARDED_BY(mLock) = 0;
@@ -154,6 +181,9 @@
     std::shared_ptr<RecurrentTimer> mRecurrentTimer;
     std::unordered_map<int32_t, std::unordered_map<int32_t, std::shared_ptr<std::function<void()>>>>
             mRecurrentActions GUARDED_BY(mLock);
+
+    std::unordered_set<PropIdAreaId, PropIdAreaIdHash> mSubscribedSupportedValueChangePropIdAreaIds
+            GUARDED_BY(mLock);
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/current/vhal/test/SubscriptionManagerTest.cpp b/automotive/vehicle/aidl/impl/current/vhal/test/SubscriptionManagerTest.cpp
index 5d58de5..2ac3a73 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/test/SubscriptionManagerTest.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/test/SubscriptionManagerTest.cpp
@@ -351,6 +351,10 @@
                                        std::vector<int32_t>({0, 1, 2}));
     ASSERT_TRUE(result.ok()) << "unsubscribe an unsubscribed property must do nothing";
 
+    result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(),
+                                       std::vector<int32_t>({0, 1, 2}));
+    ASSERT_TRUE(result.ok()) << "retry an unsubscribe operation must not throw error";
+
     std::vector<VehiclePropValue> updatedValues = {
             {
                     .prop = 0,
diff --git a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
index 43675ff..797be73 100644
--- a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -596,8 +596,33 @@
     /**
      * Tire pressure
      *
+     * Each tire is identified by its areaConfig.areaId config and its minFloatValue/maxFloatValue
+     * are used to store OEM recommended pressure range.
+     *
+     * The minFloatValue and maxFloatValue in VehicleAreaConfig must be defined.
+     *
+     * The minFloatValue in the areaConfig data represents the lower bound of the recommended tire
+     * pressure.
+     *
+     * The maxFloatValue in the areaConfig data represents the upper bound of the recommended tire
+     * pressure.
+     *
+     * For example:
+     *
+     * The following areaConfig indicates the recommended tire pressure
+     * of the left_front tire is from 200.0 KILOPASCAL to 240.0 KILOPASCAL.
+     * .areaConfigs = {
+     *      VehicleAreaConfig {
+     *          .areaId = VehicleAreaWheel::LEFT_FRONT,
+     *          .minFloatValue = 200.0,
+     *          .maxFloatValue = 240.0,
+     *      }
+     * }
+     *
+     * If {@code HasSupportedValueInfo} for a specific area ID is not {@code null}:
+     *
      * {@code HasSupportedValueInfo.hasMinSupportedValue} and
-     * {@code HasSupportedValueInfo.hasMaxSupportedValue} must be {@code true} for all areas.
+     * {@code HasSupportedValueInfo.hasMaxSupportedValue} must be {@code true} for the area ID.
      *
      * {@code MinMaxSupportedValueResult.minSupportedValue} represents the lower bound of the
      * recommended tire pressure for the tire at the specified area ID.
@@ -611,19 +636,8 @@
      * {@code MinMaxSupportedValueResult} with OK status, 200.0 as minSupportedValue, 240.0 as
      * maxSupportedValue.
      *
-     * For backward compatibility, minFloatValue and maxFloatValue in {@code VehicleAreaConfig}
-     * must be set to the same as minSupportedValue and maxSupportedValue at boot time.
-     *
-     * Each tire is identified by its areaConfig.areaId config.
-     *
-     * For example:
-     * .areaConfigs = {
-     *      VehicleAreaConfig {
-     *          .areaId = VehicleAreaWheel::LEFT_FRONT,
-     *          .minFloatValue = 200.0,
-     *          .maxFloatValue = 240.0,
-     *      }
-     * },
+     * At boot, minFloatValue is equal to minSupportedValue, maxFloatValue is equal to
+     * maxSupportedValue.
      *
      * @change_mode VehiclePropertyChangeMode.CONTINUOUS
      * @access VehiclePropertyAccess.READ
@@ -766,12 +780,15 @@
      * of the vehicle as described through the ImpactSensorLocation enum. As a bit flag property,
      * this property can be set to multiple ORed together values of the enum when necessary.
      *
-     * For the global area ID (0), {@code getSupportedValuesList}
-     * must return a {@code SupportedValuesListResult} that contains supported values unless all bit
-     * flags of ImpactSensorLocation are supported.
+     * For the global area ID (0), the VehicleAreaConfig#supportedEnumValues array must be defined
+     * unless all bit flags of ImpactSensorLocation are supported.
      *
-     * For backward compatibility, if {@code SupportedValuesListResult} is defined,
-     * {@code VehicleAreaConfig#supportedEnumValues} must be set to the same values.
+     * If {@code HasSupportedValueInfo} is not {@code null} for the global area ID (0):
+     *
+     * {@code getSupportedValuesList} must return a {@code SupportedValuesListResult} that contains
+     * supported values unless all bit flags of ImpactSensorLocation are supported.
+     *
+     * At boot, supportedEnumValues is equal to the supported values list.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ
@@ -798,6 +815,17 @@
      *
      * This is the gear selected by the user.
      *
+     * Values in the config array must represent the list of supported gears for this vehicle at
+     * boot time. For example, config array for an automatic transmission must contain
+     * {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_PARK, GEAR_DRIVE, GEAR_1, GEAR_2,...} and for manual
+     * transmission the list must contain {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_1, GEAR_2,...}
+     *
+     * In the case of an automatic transmission vehicle that allows the driver to select specific
+     * gears on demand (i.e. "manual mode"), GEAR_SELECTION's value must be set to the specific gear
+     * selected by the driver instead of simply GEAR_DRIVE.
+     *
+     * If {@code HasSupportedValueInfo} is not {@code null} for the global area ID (0):
+     *
      * {@code VehicleAreaConfig.HasSupportedValueInfo.hasSupportedValuesList} for the global area ID
      * (0) must be {@code true}.
      *
@@ -810,11 +838,10 @@
      * GEAR_REVERSE, GEAR_1, GEAR_2,...}.
      *
      * In the case of an automatic transmission vehicle that allows the driver to select specific
-     * gears on demand (i.e. "manual mode"), GEAR_SELECTION's value must be set to the specific gear
-     * selected by the driver instead of simply GEAR_DRIVE.
+     * gears on demand (i.e. "manual mode"), the GEAR_SELECTION property value must be set to the
+     * specific gear selected by the driver instead of simply GEAR_DRIVE.
      *
-     * For backward compatibility, config array for this property must be a list of values
-     * same as the supported values at boot-time.
+     * At boot, the config array's values are equal to the supported values list.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ
@@ -831,6 +858,15 @@
      * the current gear will be one of GEAR_1, GEAR_2 etc, which reflects
      * the actual gear the transmission is currently running in.
      *
+     * Values in the config array must represent the list of supported gears
+     * for this vehicle at boot time.  For example, config array for an automatic transmission
+     * must contain {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_PARK, GEAR_1, GEAR_2,...}
+     * and for manual transmission the list must contain
+     * {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_1, GEAR_2,...}. This list need not be the
+     * same as that of the supported gears reported in GEAR_SELECTION.
+     *
+     * If {@code HasSupportedValueInfo} is not {@code null} for the global area ID (0):
+     *
      * {@code VehicleAreaConfig.HasSupportedValueInfo.hasSupportedValuesList} for the global area ID
      * (0) must be {@code true}.
      *
@@ -844,8 +880,7 @@
      * {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_1, GEAR_2,...}. This list need not be the
      * same as that of the supported gears reported in GEAR_SELECTION.
      *
-     * For backward compatibility, config array for this property must be a list of values
-     * same as the supported values at boot-time.
+     * At boot, the config array's values are equal to the supported values list.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ
@@ -889,6 +924,18 @@
     /**
      * Regenerative braking level of a electronic vehicle
      *
+     * The minInt32Value and maxInt32Value in VehicleAreaConfig must be defined. All values between
+     * minInt32Value and maxInt32Value must be supported.
+     *
+     * The minInt32Value indicates the setting for no regenerative braking, must be 0.
+     *
+     * The maxInt32Value indicates the setting for the maximum amount of energy regenerated from
+     * braking.
+     *
+     * All values between min and max supported value must be supported.
+     *
+     * If {@code HasSupportedValueInfo} is not {@code null} for the global area ID (0):
+     *
      * {@code HasSupportedValueInfo.hasMinSupportedValue} and
      * {@code HasSupportedValueInfo.hasMaxSupportedValue} must be {@code true} for global area ID(0)
      *
@@ -898,10 +945,9 @@
      * amount of energy regenerated from braking. The minSupportedValue indicates the setting for no
      * regenerative braking.
      *
-     * All values between min and max supported value must be supported.
+     * At boot, minInt32Value is equal to minSupportedValue, maxInt32Value is equal to
+     * maxSupportedValue.
      *
-     * For backward compatibility, minInt32Value and maxInt32Value in {@code VehicleAreaConfig}
-     * must be set to the same as minSupportedValue and maxSupportedValue at boot time.
      *
      * This property is a more granular form of EV_REGENERATIVE_BRAKING_STATE. It allows the user to
      * set a more specific level of regenerative braking if the states in EvRegenerativeBrakingState
@@ -1004,12 +1050,17 @@
     /**
      * Represents property for the current stopping mode of the vehicle.
      *
+     * For the global area ID (0), the VehicleAreaConfig#supportedEnumValues must be defined unless
+     * all enum values of EvStoppingMode are supported.
+     *
+     * If {@code HasSupportedValueInfo} is not {@code null} for the global area ID (0):
+     *
      * For the global area ID (0), {@code getSupportedValuesList}
      * must return a {@code SupportedValuesListResult} that contains supported values unless all
      * enum values of EvStoppingMode are supported.
      *
-     * For backward compatibility, if {@code SupportedValuesListResult} is defined,
-     * {@code VehicleAreaConfig#supportedEnumValues} must be set to the same values.
+     * At boot, supportedEnumValues is equal to the supported values list.
+     *
      *
      * The EvStoppingMode enum may be extended to include more states in the future.
      *
@@ -1053,13 +1104,18 @@
      * ElectronicStabilityControlState or ErrorState. It must not surface errors through StatusCode
      * and must use the supported error states instead.
      *
+     * For the global area ID (0), the VehicleAreaConfig#supportedEnumValues array must be defined
+     * unless all states of both ElectronicStabilityControlState (including OTHER, which is not
+     * recommended) and ErrorState are supported.
+     *
+     * If {@code HasSupportedValueInfo} is not {@code null} for the global area ID (0):
+     *
      * For the global area ID (0), {@code getSupportedValuesList}
      * must return a {@code SupportedValuesListResult} that contains supported values unless all
      * states of both ElectronicStabilityControlState (including OTHER, which is not
      * recommended) and ErrorState are supported.
      *
-     * For backward compatibility, if {@code SupportedValuesListResult} is defined,
-     * {@code VehicleAreaConfig#supportedEnumValues} must be set to the same values.
+     * At boot, supportedEnumValues is equal to the supported values list.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ
@@ -1178,6 +1234,22 @@
      * The minInt32Value indicates the lowest fan speed.
      * The maxInt32Value indicates the highest fan speed.
      *
+     * If {@code HasSupportedValueInfo} for a specific area ID is not {@code null}:
+     *
+     * {@code HasSupportedValueInfo.hasMinSupportedValue} and
+     * {@code HasSupportedValueInfo.hasMaxSupportedValue} must be {@code true} for the specific
+     * area ID.
+     *
+     * {@code MinMaxSupportedValueResult.minSupportedValue} indicates the lowest fan speed.
+     *
+     * {@code MinMaxSupportedValueResult.maxSupportedValue} indicates the highest fan speed.
+     *
+     * All integers between minSupportedValue and maxSupportedValue must be supported.
+     *
+     * At boot, minInt32Value is equal to minSupportedValue, maxInt32Value is equal to
+     * maxSupportedValue.
+     *
+     *
      * This property is not in any particular unit but in a specified range of relative speeds.
      *
      * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
@@ -1186,6 +1258,7 @@
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @access VehiclePropertyAccess.READ
+     * @require_min_max_supported_value
      * @version 2
      */
     HVAC_FAN_SPEED = 0x0500 + 0x10000000 + 0x05000000
@@ -1840,6 +1913,14 @@
      *
      * Indicates type of units the car is using to display speed to user. Eg. m/s, km/h, or mph.
      *
+     * VehiclePropConfig.configArray is used to indicate the supported speed display units.
+     * Pressure units are defined in VehicleUnit.
+     * For example: configArray[0] = METER_PER_SEC
+     *              configArray[1] = MILES_PER_HOUR
+     *              configArray[2] = KILOMETERS_PER_HOUR
+     *
+     * If {@code HasSupportedValueInfo} is not {@code null} for the global area ID (0):
+     *
      * {@code VehicleAreaConfig.HasSupportedValueInfo.hasSupportedValuesList} for the global area ID
      * (0) must be {@code true}.
      *
@@ -1847,11 +1928,8 @@
      * {@code SupportedValuesListResult} that contains non-null {@code supportedValuesList},
      * e.g. [METER_PER_SEC, MILES_PER_HOUR, KILOMETERS_PER_HOUR].
      *
-     * For backward compatibility, config array for this property must contain the same values as
-     * supported values at boot time.
-     * For example: configArray[0] = METER_PER_SEC
-     *              configArray[1] = MILES_PER_HOUR
-     *              configArray[2] = KILOMETERS_PER_HOUR
+     * At boot, the values in the config array are equal to the supported values list.
+     *
      *
      * If updating VEHICLE_SPEED_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS
      * properties, then their values must be updated and communicated to the AAOS framework as well.
diff --git a/biometrics/face/aidl/default/Android.bp b/biometrics/face/aidl/default/Android.bp
index bed0405..dc11af6 100644
--- a/biometrics/face/aidl/default/Android.bp
+++ b/biometrics/face/aidl/default/Android.bp
@@ -78,7 +78,7 @@
     vendor: true,
     relative_install_path: "hw",
     init_rc: ["face-default.rc"],
-    vintf_fragments: ["face-default.xml"],
+    vintf_fragment_modules: ["android.hardware.biometrics.face-service.default.vintf"],
     shared_libs: [
         "libbinder_ndk",
         "liblog",
@@ -89,6 +89,12 @@
     ],
 }
 
+vintf_fragment {
+    name: "android.hardware.biometrics.face-service.default.vintf",
+    src: "face-default.xml",
+    vendor: true,
+}
+
 sysprop_library {
     name: "android.hardware.biometrics.face.VirtualProps",
     srcs: ["face.sysprop"],
diff --git a/biometrics/fingerprint/aidl/default/Android.bp b/biometrics/fingerprint/aidl/default/Android.bp
index faaa9c6..c6ffc51 100644
--- a/biometrics/fingerprint/aidl/default/Android.bp
+++ b/biometrics/fingerprint/aidl/default/Android.bp
@@ -83,7 +83,7 @@
     vendor: true,
     relative_install_path: "hw",
     init_rc: ["fingerprint-default.rc"],
-    vintf_fragments: ["fingerprint-default.xml"],
+    vintf_fragment_modules: ["android.hardware.biometrics.fingerprint-service.default.vintf"],
     local_include_dirs: ["include"],
     srcs: [
     ],
@@ -105,6 +105,12 @@
     ],
 }
 
+vintf_fragment {
+    name: "android.hardware.biometrics.fingerprint-service.default.vintf",
+    src: "fingerprint-default.xml",
+    vendor: true,
+}
+
 cc_test {
     name: "android.hardware.biometrics.fingerprint.FakeFingerprintEngineTest",
     local_include_dirs: ["include"],
diff --git a/biometrics/fingerprint/aidl/default/Fingerprint.cpp b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
index 3055da1..143e231 100644
--- a/biometrics/fingerprint/aidl/default/Fingerprint.cpp
+++ b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
@@ -50,6 +50,9 @@
     } else if (sensorTypeProp == "udfps") {
         mSensorType = FingerprintSensorType::UNDER_DISPLAY_OPTICAL;
         mEngine = std::make_unique<FakeFingerprintEngineUdfps>();
+    } else if (sensorTypeProp == "udfps-us") {
+        mSensorType = FingerprintSensorType::UNDER_DISPLAY_ULTRASONIC;
+        mEngine = std::make_unique<FakeFingerprintEngineUdfps>();
     } else if (sensorTypeProp == "side") {
         mSensorType = FingerprintSensorType::POWER_BUTTON;
         mEngine = std::make_unique<FakeFingerprintEngineSide>();
@@ -220,7 +223,7 @@
         case FingerprintSensorType::UNDER_DISPLAY_OPTICAL:
             return "udfps";
         case FingerprintSensorType::UNDER_DISPLAY_ULTRASONIC:
-            return "udfps";
+            return "udfps-us";
         default:
             return "unknown";
     }
diff --git a/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt b/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt
index 8c02a68..ad6f9e0 100644
--- a/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt
+++ b/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt
@@ -173,6 +173,6 @@
     type: String
     access: ReadWrite
     prop_name: "persist.vendor.fingerprint.virtual.type"
-    enum_values: "default|rear|udfps|side"
+    enum_values: "default|rear|udfps|udfps-us|side"
   }
 }
diff --git a/biometrics/fingerprint/aidl/default/fingerprint.sysprop b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
index eb33432..1d64c48 100644
--- a/biometrics/fingerprint/aidl/default/fingerprint.sysprop
+++ b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
@@ -9,7 +9,7 @@
     type: String
     scope: Public
     access: ReadWrite
-    enum_values: "default|rear|udfps|side"
+    enum_values: "default|rear|udfps|udfps-us|side"
     api_name: "type"
 }
 
diff --git a/biometrics/fingerprint/aidl/default/tests/VirtualHalTest.cpp b/biometrics/fingerprint/aidl/default/tests/VirtualHalTest.cpp
index 8ffc96b..25abffe 100644
--- a/biometrics/fingerprint/aidl/default/tests/VirtualHalTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/VirtualHalTest.cpp
@@ -152,7 +152,7 @@
     } typeMap[] = {{FingerprintSensorType::REAR, "rear"},
                    {FingerprintSensorType::POWER_BUTTON, "side"},
                    {FingerprintSensorType::UNDER_DISPLAY_OPTICAL, "udfps"},
-                   {FingerprintSensorType::UNDER_DISPLAY_ULTRASONIC, "udfps"},
+                   {FingerprintSensorType::UNDER_DISPLAY_ULTRASONIC, "udfps-us"},
                    {FingerprintSensorType::UNKNOWN, "unknown"}};
     for (auto const& x : typeMap) {
         mVhal->setType(x.type);
diff --git a/bluetooth/1.0/vts/functional/Android.bp b/bluetooth/1.0/vts/functional/Android.bp
index 7d76b89..e68df09 100644
--- a/bluetooth/1.0/vts/functional/Android.bp
+++ b/bluetooth/1.0/vts/functional/Android.bp
@@ -30,7 +30,6 @@
     srcs: ["VtsHalBluetoothV1_0TargetTest.cpp"],
     static_libs: [
         "android.hardware.bluetooth@1.0",
-        "libbluetooth-types",
     ],
     test_config: "VtsHalBluetoothV1_0TargetTest.xml",
     test_suites: [
diff --git a/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp b/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp
index 82dda61..d0edfad 100644
--- a/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp
+++ b/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp
@@ -20,7 +20,6 @@
 #include <android/hardware/bluetooth/1.0/IBluetoothHci.h>
 #include <android/hardware/bluetooth/1.0/IBluetoothHciCallbacks.h>
 #include <android/hardware/bluetooth/1.0/types.h>
-#include <hardware/bluetooth.h>
 #include <utils/Log.h>
 
 #include <VtsHalHidlTargetCallbackBase.h>
diff --git a/bluetooth/1.1/vts/functional/Android.bp b/bluetooth/1.1/vts/functional/Android.bp
index 6b3a3e7..27d50f5 100644
--- a/bluetooth/1.1/vts/functional/Android.bp
+++ b/bluetooth/1.1/vts/functional/Android.bp
@@ -31,7 +31,6 @@
     static_libs: [
         "android.hardware.bluetooth@1.1",
         "android.hardware.bluetooth@1.0",
-        "libbluetooth-types",
     ],
     test_config: "VtsHalBluetoothV1_1TargetTest.xml",
     test_suites: ["general-tests", "vts"],
diff --git a/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.cpp b/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.cpp
index 687765f..37b41c6 100644
--- a/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.cpp
+++ b/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.cpp
@@ -20,7 +20,6 @@
 #include <android/hardware/bluetooth/1.0/types.h>
 #include <android/hardware/bluetooth/1.1/IBluetoothHci.h>
 #include <android/hardware/bluetooth/1.1/IBluetoothHciCallbacks.h>
-#include <hardware/bluetooth.h>
 #include <utils/Log.h>
 
 #include <VtsHalHidlTargetCallbackBase.h>
diff --git a/bluetooth/OWNERS b/bluetooth/OWNERS
index f401b8c..df250c8 100644
--- a/bluetooth/OWNERS
+++ b/bluetooth/OWNERS
@@ -1,5 +1,5 @@
 # Bug component: 27441
 
+asoulier@google.com
 henrichataing@google.com
-mylesgw@google.com
 siyuanh@google.com
diff --git a/bluetooth/aidl/Android.bp b/bluetooth/aidl/Android.bp
index c6a592f..721be73 100644
--- a/bluetooth/aidl/Android.bp
+++ b/bluetooth/aidl/Android.bp
@@ -23,6 +23,9 @@
             // translate code.
             enabled: true,
         },
+        rust: {
+            enabled: true,
+        },
         java: {
             sdk_version: "module_current",
         },
diff --git a/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
index 4d90058..51931e7 100644
--- a/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
+++ b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
@@ -397,12 +397,18 @@
   while (!event_queue.empty()) {
     std::vector<uint8_t> event;
     event_queue.front(event);
-    auto complete_view = ::bluetooth::hci::CommandCompleteView::Create(
+
+    auto event_view =
         ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
-            std::make_shared<std::vector<uint8_t>>(event))));
-    auto status_view = ::bluetooth::hci::CommandCompleteView::Create(
-        ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
-            std::make_shared<std::vector<uint8_t>>(event))));
+            std::make_shared<std::vector<uint8_t>>(event)));
+    if (!event_view.IsValid()) {
+      break;
+    }
+
+    auto status_view = ::bluetooth::hci::CommandStatusView::Create(event_view);
+    auto complete_view =
+        ::bluetooth::hci::CommandCompleteView::Create(event_view);
+
     bool is_complete_no_op =
         complete_view.IsValid() &&
         complete_view.GetCommandOpCode() == ::bluetooth::hci::OpCode::NONE;
diff --git a/bluetooth/audio/aidl/Android.bp b/bluetooth/audio/aidl/Android.bp
index ae55fa9..dbff368 100644
--- a/bluetooth/audio/aidl/Android.bp
+++ b/bluetooth/audio/aidl/Android.bp
@@ -38,6 +38,9 @@
         cpp: {
             enabled: false,
         },
+        rust: {
+            enabled: true,
+        },
         java: {
             sdk_version: "module_current",
             enabled: false,
@@ -86,7 +89,6 @@
 
     ],
     frozen: false,
-
 }
 
 // Note: This should always be one version ahead of the last frozen version
diff --git a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
index 18fc4b2..3f1f5f6 100644
--- a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
+++ b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
@@ -489,11 +489,11 @@
 std::vector<AseDirectionConfiguration> getValidConfigurationsFromAllocation(
     int req_allocation_bitmask,
     std::vector<AseDirectionConfiguration>& valid_direction_configurations,
-    bool is_exact) {
+    bool isExact) {
   // Prefer the same allocation_bitmask
   int channel_count = getCountFromBitmask(req_allocation_bitmask);
 
-  if (is_exact) {
+  if (isExact) {
     for (auto& cfg : valid_direction_configurations) {
       int cfg_bitmask =
           getLeAudioAseConfigurationAllocationBitmask(cfg.aseConfiguration);
@@ -747,12 +747,19 @@
     std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>&
         matched_ase_configuration_settings,
     const IBluetoothAudioProvider::LeAudioConfigurationRequirement& requirement,
-    bool isMatchContext, bool isExact) {
+    bool isMatchContext, bool isExact, bool isMatchFlags) {
   LOG(INFO) << __func__ << ": Trying to match for the requirement "
             << requirement.toString() << ", match context = " << isMatchContext
-            << ", match exact = " << isExact;
+            << ", match exact = " << isExact
+            << ", match flags = " << isMatchFlags;
+  // Don't have to match with flag if requirements don't have flags.
+  auto requirement_flags_bitmask = 0;
+  if (isMatchFlags) {
+    if (!requirement.flags.has_value()) return std::nullopt;
+    requirement_flags_bitmask = requirement.flags.value().bitmask;
+  }
   for (auto& setting : matched_ase_configuration_settings) {
-    // Try to match context in metadata.
+    // Try to match context.
     if (isMatchContext) {
       if ((setting.audioContext.bitmask & requirement.audioContext.bitmask) !=
           requirement.audioContext.bitmask)
@@ -761,6 +768,16 @@
                  << getSettingOutputString(setting);
     }
 
+    // Try to match configuration flags
+    if (isMatchFlags) {
+      if (!setting.flags.has_value()) continue;
+      if ((setting.flags.value().bitmask & requirement_flags_bitmask) !=
+          requirement_flags_bitmask)
+        continue;
+      LOG(DEBUG) << __func__ << ": Setting with matched flags: "
+                 << getSettingOutputString(setting);
+    }
+
     auto filtered_ase_configuration_setting =
         getRequirementMatchedAseConfigurationSettings(setting, requirement,
                                                       isExact);
@@ -853,21 +870,25 @@
     // If we cannot match, return an empty result.
 
     // Matching priority list:
+    // Matched configuration flags, i.e. for asymmetric requirement.
     // Preferred context - exact match with allocation
     // Preferred context - loose match with allocation
     // Any context - exact match with allocation
     // Any context - loose match with allocation
     bool found = false;
-    for (bool match_context : {true, false}) {
-      for (bool match_exact : {true, false}) {
-        auto matched_setting =
-            matchWithRequirement(matched_ase_configuration_settings,
-                                 requirement, match_context, match_exact);
-        if (matched_setting.has_value()) {
-          result.push_back(matched_setting.value());
-          found = true;
-          break;
+    for (bool match_flag : {true, false}) {
+      for (bool match_context : {true, false}) {
+        for (bool match_exact : {true, false}) {
+          auto matched_setting = matchWithRequirement(
+              matched_ase_configuration_settings, requirement, match_context,
+              match_exact, match_flag);
+          if (matched_setting.has_value()) {
+            result.push_back(matched_setting.value());
+            found = true;
+            break;
+          }
         }
+        if (found) break;
       }
       if (found) break;
     }
@@ -881,7 +902,11 @@
     }
   }
 
-  LOG(INFO) << __func__ << ": Found matches for all requirements!";
+  LOG(INFO) << __func__
+            << ": Found matches for all requirements, chosen settings:";
+  for (auto& setting : result) {
+    LOG(INFO) << __func__ << ": " << getSettingOutputString(setting);
+  }
   *_aidl_return = result;
   return ndk::ScopedAStatus::ok();
 };
@@ -913,7 +938,13 @@
     const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
         qosRequirement,
     std::vector<LeAudioAseConfigurationSetting>& ase_configuration_settings,
-    bool is_exact) {
+    bool isExact, bool isMatchFlags) {
+  auto requirement_flags_bitmask = 0;
+  if (isMatchFlags) {
+    if (!qosRequirement.flags.has_value()) return std::nullopt;
+    requirement_flags_bitmask = qosRequirement.flags.value().bitmask;
+  }
+
   std::optional<AseQosDirectionRequirement> direction_qos_requirement =
       std::nullopt;
 
@@ -929,9 +960,18 @@
     if ((setting.audioContext.bitmask & qosRequirement.audioContext.bitmask) !=
         qosRequirement.audioContext.bitmask)
       continue;
+    LOG(DEBUG) << __func__ << ": Setting with matched context: "
+               << getSettingOutputString(setting);
 
     // Match configuration flags
-    // Currently configuration flags are not populated, ignore.
+    if (isMatchFlags) {
+      if (!setting.flags.has_value()) continue;
+      if ((setting.flags.value().bitmask & requirement_flags_bitmask) !=
+          requirement_flags_bitmask)
+        continue;
+      LOG(DEBUG) << __func__ << ": Setting with matched flags: "
+                 << getSettingOutputString(setting);
+    }
 
     // Get a list of all matched AseDirectionConfiguration
     // for the input direction
@@ -980,7 +1020,7 @@
         direction_qos_requirement.value().aseConfiguration);
     // Get the best matching config based on channel allocation
     auto req_valid_configs = getValidConfigurationsFromAllocation(
-        qos_allocation_bitmask, temp, is_exact);
+        qos_allocation_bitmask, temp, isExact);
     if (req_valid_configs.empty()) {
       LOG(WARNING) << __func__
                    << ": Cannot find matching allocation for bitmask "
@@ -1010,29 +1050,38 @@
   if (in_qosRequirement.sinkAseQosRequirement.has_value()) {
     if (!isValidQosRequirement(in_qosRequirement.sinkAseQosRequirement.value()))
       return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    {
-      // Try exact match first
-      result.sinkQosConfiguration =
-          getDirectionQosConfiguration(kLeAudioDirectionSink, in_qosRequirement,
-                                       ase_configuration_settings, true);
-      if (!result.sinkQosConfiguration.has_value()) {
-        result.sinkQosConfiguration = getDirectionQosConfiguration(
+    bool found = false;
+    for (bool match_flag : {true, false}) {
+      for (bool match_exact : {true, false}) {
+        auto setting = getDirectionQosConfiguration(
             kLeAudioDirectionSink, in_qosRequirement,
-            ase_configuration_settings, false);
+            ase_configuration_settings, match_exact, match_flag);
+        if (setting.has_value()) {
+          found = true;
+          result.sinkQosConfiguration = setting;
+          break;
+        }
       }
+      if (found) break;
     }
   }
   if (in_qosRequirement.sourceAseQosRequirement.has_value()) {
     if (!isValidQosRequirement(
             in_qosRequirement.sourceAseQosRequirement.value()))
       return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    result.sourceQosConfiguration =
-        getDirectionQosConfiguration(kLeAudioDirectionSource, in_qosRequirement,
-                                     ase_configuration_settings, true);
-    if (!result.sourceQosConfiguration.has_value()) {
-      result.sourceQosConfiguration = getDirectionQosConfiguration(
-          kLeAudioDirectionSource, in_qosRequirement,
-          ase_configuration_settings, false);
+    bool found = false;
+    for (bool match_flag : {true, false}) {
+      for (bool match_exact : {true, false}) {
+        auto setting = getDirectionQosConfiguration(
+            kLeAudioDirectionSource, in_qosRequirement,
+            ase_configuration_settings, match_exact, match_flag);
+        if (setting.has_value()) {
+          found = true;
+          result.sourceQosConfiguration = setting;
+          break;
+        }
+      }
+      if (found) break;
     }
   }
 
diff --git a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h
index 3a82b73..8c4f543 100644
--- a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h
+++ b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h
@@ -164,7 +164,7 @@
       const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
           qosRequirement,
       std::vector<LeAudioAseConfigurationSetting>& ase_configuration_settings,
-      bool is_exact);
+      bool isExact, bool isMatchedFlag);
   bool isSubgroupConfigurationMatchedContext(
       AudioContext requirement_context,
       IBluetoothAudioProvider::BroadcastQuality quality,
@@ -175,7 +175,7 @@
           matched_ase_configuration_settings,
       const IBluetoothAudioProvider::LeAudioConfigurationRequirement&
           requirements,
-      bool isMatchContext, bool isExact);
+      bool isMatchContext, bool isExact, bool isMatchFlags);
 };
 
 class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider {
diff --git a/bluetooth/audio/utils/Android.bp b/bluetooth/audio/utils/Android.bp
index d4968a8..6d1da63 100644
--- a/bluetooth/audio/utils/Android.bp
+++ b/bluetooth/audio/utils/Android.bp
@@ -90,15 +90,15 @@
 
 cc_test {
     name: "BluetoothLeAudioCodecsProviderTest",
-    srcs: [
-        "aidl_session/BluetoothLeAudioCodecsProvider.cpp",
-        "aidl_session/BluetoothLeAudioCodecsProviderTest.cpp",
-    ],
     defaults: [
         "latest_android_hardware_audio_common_ndk_static",
         "latest_android_hardware_bluetooth_audio_ndk_static",
         "latest_android_media_audio_common_types_ndk_static",
     ],
+    srcs: [
+        "aidl_session/BluetoothLeAudioCodecsProvider.cpp",
+        "aidl_session/BluetoothLeAudioCodecsProviderTest.cpp",
+    ],
     header_libs: [
         "libxsdc-utils",
     ],
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.cpp b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.cpp
index 07e4997..5909c92 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.cpp
@@ -47,6 +47,8 @@
 #include <aidl/android/hardware/bluetooth/audio/Phy.h>
 #include <android-base/logging.h>
 
+#include <optional>
+
 #include "flatbuffers/idl.h"
 #include "flatbuffers/util.h"
 
@@ -561,6 +563,17 @@
   if (ase_cnt == 2) directionAseConfiguration.push_back(config);
 }
 
+// Comparing if 2 AseDirectionConfiguration is equal.
+// Configuration are copied in, so we can remove some fields for comparison
+// without affecting the result.
+bool isAseConfigurationEqual(AseDirectionConfiguration cfg_a,
+                             AseDirectionConfiguration cfg_b) {
+  // Remove unneeded fields when comparing.
+  cfg_a.aseConfiguration.metadata = std::nullopt;
+  cfg_b.aseConfiguration.metadata = std::nullopt;
+  return cfg_a == cfg_b;
+}
+
 void AudioSetConfigurationProviderJson::PopulateAseConfigurationFromFlat(
     const le_audio::AudioSetConfiguration* flat_cfg,
     std::vector<const le_audio::CodecConfiguration*>* codec_cfgs,
@@ -569,7 +582,7 @@
     std::vector<std::optional<AseDirectionConfiguration>>&
         sourceAseConfiguration,
     std::vector<std::optional<AseDirectionConfiguration>>& sinkAseConfiguration,
-    ConfigurationFlags& /*configurationFlags*/) {
+    ConfigurationFlags& configurationFlags) {
   if (flat_cfg == nullptr) {
     LOG(ERROR) << "flat_cfg cannot be null";
     return;
@@ -636,17 +649,41 @@
                          sourceAseConfiguration, location);
       }
     }
-  } else {
-    if (codec_cfg == nullptr) {
-      LOG(ERROR) << "No codec config matching key " << codec_config_key.c_str()
-                 << " found";
+
+    // After putting all subconfig, check if it's an asymmetric configuration
+    // and populate information for ConfigurationFlags
+    if (!sinkAseConfiguration.empty() && !sourceAseConfiguration.empty()) {
+      if (sinkAseConfiguration.size() == sourceAseConfiguration.size()) {
+        for (int i = 0; i < sinkAseConfiguration.size(); ++i) {
+          if (sinkAseConfiguration[i].has_value() !=
+              sourceAseConfiguration[i].has_value()) {
+            // Different configuration: one is not empty and other is.
+            configurationFlags.bitmask |=
+                ConfigurationFlags::ALLOW_ASYMMETRIC_CONFIGURATIONS;
+          } else if (sinkAseConfiguration[i].has_value()) {
+            // Both is not empty, comparing inner fields:
+            if (!isAseConfigurationEqual(sinkAseConfiguration[i].value(),
+                                         sourceAseConfiguration[i].value())) {
+              configurationFlags.bitmask |=
+                  ConfigurationFlags::ALLOW_ASYMMETRIC_CONFIGURATIONS;
+            }
+          }
+        }
+      } else {
+        // Different number of ASE, is a different configuration.
+        configurationFlags.bitmask |=
+            ConfigurationFlags::ALLOW_ASYMMETRIC_CONFIGURATIONS;
+      }
     } else {
-      LOG(ERROR) << "Configuration '" << flat_cfg->name()->c_str()
-                 << "' has no valid subconfigurations.";
+      if (codec_cfg == nullptr) {
+        LOG(ERROR) << "No codec config matching key "
+                   << codec_config_key.c_str() << " found";
+      } else {
+        LOG(ERROR) << "Configuration '" << flat_cfg->name()->c_str()
+                   << "' has no valid subconfigurations.";
+      }
     }
   }
-
-  // TODO: Populate information for ConfigurationFlags
 }
 
 bool AudioSetConfigurationProviderJson::LoadConfigurationsFromFiles(
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp
index 59c43a4..a96df52 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
+#include <optional>
 #include <set>
 
 #include "aidl/android/hardware/bluetooth/audio/ChannelMode.h"
 #include "aidl/android/hardware/bluetooth/audio/CodecId.h"
+#include "aidl/android/hardware/bluetooth/audio/CodecInfo.h"
+#include "aidl/android/hardware/bluetooth/audio/ConfigurationFlags.h"
 #include "aidl_android_hardware_bluetooth_audio_setting_enums.h"
 #define LOG_TAG "BTAudioCodecsProviderAidl"
 
@@ -52,6 +55,26 @@
   return le_audio_offload_setting;
 }
 
+void add_flag(CodecInfo& codec_info, int32_t bitmask) {
+  auto& transport =
+      codec_info.transport.get<CodecInfo::Transport::Tag::leAudio>();
+  if (!transport.flags.has_value()) transport.flags = ConfigurationFlags();
+  transport.flags->bitmask |= bitmask;
+}
+
+// Compare 2 codec info to see if they are equal.
+// Currently only compare bitdepth, frameDurationUs and samplingFrequencyHz
+bool is_equal(CodecInfo& codec_info_a, CodecInfo& codec_info_b) {
+  auto& transport_a =
+      codec_info_a.transport.get<CodecInfo::Transport::Tag::leAudio>();
+  auto& transport_b =
+      codec_info_b.transport.get<CodecInfo::Transport::Tag::leAudio>();
+  return codec_info_a.name == codec_info_b.name &&
+         transport_a.bitdepth == transport_b.bitdepth &&
+         transport_a.frameDurationUs == transport_b.frameDurationUs &&
+         transport_a.samplingFrequencyHz == transport_b.samplingFrequencyHz;
+}
+
 std::unordered_map<SessionType, std::vector<CodecInfo>>
 BluetoothLeAudioCodecsProvider::GetLeAudioCodecInfo(
     const std::optional<setting::LeAudioOffloadSetting>&
@@ -111,6 +134,9 @@
     codec_info.transport =
         CodecInfo::Transport::make<CodecInfo::Transport::Tag::leAudio>();
 
+    // Add low latency support by default
+    add_flag(codec_info, ConfigurationFlags::LOW_LATENCY);
+
     // Mapping codec configuration information
     auto& transport =
         codec_info.transport.get<CodecInfo::Transport::Tag::leAudio>();
@@ -152,6 +178,25 @@
     }
   }
 
+  // Goes through a list of scenarios and detect asymmetrical config using
+  // codecConfiguration name.
+  for (auto& s : supported_scenarios_) {
+    if (s.hasEncode() && s.hasDecode() &&
+        config_codec_info_map_.count(s.getEncode()) &&
+        config_codec_info_map_.count(s.getDecode())) {
+      // Check if it's actually using the different codec
+      auto& encode_codec_info = config_codec_info_map_[s.getEncode()];
+      auto& decode_codec_info = config_codec_info_map_[s.getDecode()];
+      if (!is_equal(encode_codec_info, decode_codec_info)) {
+        // Change both x and y to become asymmetrical
+        add_flag(encode_codec_info,
+                 ConfigurationFlags::ALLOW_ASYMMETRIC_CONFIGURATIONS);
+        add_flag(decode_codec_info,
+                 ConfigurationFlags::ALLOW_ASYMMETRIC_CONFIGURATIONS);
+      }
+    }
+  }
+
   // Goes through every scenario, deduplicate configuration, skip the invalid
   // config references (e.g. the "invalid" entries in the xml file).
   std::set<std::string> encoding_config, decoding_config, broadcast_config;
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProviderTest.cpp b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProviderTest.cpp
index c47f7d5..6338e11 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProviderTest.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProviderTest.cpp
@@ -20,10 +20,16 @@
 #include <tuple>
 
 #include "BluetoothLeAudioCodecsProvider.h"
+#include "aidl/android/hardware/bluetooth/audio/CodecInfo.h"
+#include "aidl/android/hardware/bluetooth/audio/ConfigurationFlags.h"
+#include "aidl/android/hardware/bluetooth/audio/SessionType.h"
 
 using aidl::android::hardware::bluetooth::audio::BluetoothLeAudioCodecsProvider;
+using aidl::android::hardware::bluetooth::audio::CodecInfo;
+using aidl::android::hardware::bluetooth::audio::ConfigurationFlags;
 using aidl::android::hardware::bluetooth::audio::
     LeAudioCodecCapabilitiesSetting;
+using aidl::android::hardware::bluetooth::audio::SessionType;
 using aidl::android::hardware::bluetooth::audio::setting::AudioLocation;
 using aidl::android::hardware::bluetooth::audio::setting::CodecConfiguration;
 using aidl::android::hardware::bluetooth::audio::setting::
@@ -51,15 +57,30 @@
 static const Scenario kValidBroadcastScenario(
     std::nullopt, std::nullopt, std::make_optional("BcastStereo_16_2"));
 
+static const Scenario kValidAsymmetricScenario(
+    std::make_optional("OneChanStereo_32_1"),
+    std::make_optional("OneChanStereo_16_1"), std::nullopt);
+
 // Configuration
 static const Configuration kValidConfigOneChanStereo_16_1(
     std::make_optional("OneChanStereo_16_1"), std::make_optional("LC3_16k_1"),
     std::make_optional("STEREO_ONE_CIS_PER_DEVICE"));
+
+static const Configuration kValidConfigOneChanStereo_32_1(
+    std::make_optional("OneChanStereo_32_1"), std::make_optional("LC3_32k_1"),
+    std::make_optional("STEREO_ONE_CIS_PER_DEVICE"));
+
 // CodecConfiguration
 static const CodecConfiguration kValidCodecLC3_16k_1(
     std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
     std::nullopt, std::make_optional(16000), std::make_optional(7500),
     std::make_optional(30), std::nullopt);
+
+static const CodecConfiguration kValidCodecLC3_32k_1(
+    std::make_optional("LC3_32k_1"), std::make_optional(CodecType::LC3),
+    std::nullopt, std::make_optional(32000), std::make_optional(7500),
+    std::make_optional(30), std::nullopt);
+
 // StrategyConfiguration
 static const StrategyConfiguration kValidStrategyStereoOneCis(
     std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
@@ -181,6 +202,17 @@
             kValidStrategyStereoOneCisBoth, kValidStrategyStereoTwoCisBoth,
             kValidStrategyMonoOneCisBoth, kValidStrategyBroadcastStereoBoth})};
 
+// Define some valid asymmetric scenario list
+static const std::vector<ScenarioList> kValidAsymmetricScenarioList = {
+    ScenarioList(std::vector<Scenario>{kValidAsymmetricScenario})};
+static const std::vector<ConfigurationList> kValidAsymmetricConfigurationList =
+    {ConfigurationList(std::vector<Configuration>{
+        kValidConfigOneChanStereo_16_1, kValidConfigOneChanStereo_32_1})};
+static const std::vector<CodecConfigurationList>
+    kValidAsymmetricCodecConfigurationList = {
+        CodecConfigurationList(std::vector<CodecConfiguration>{
+            kValidCodecLC3_16k_1, kValidCodecLC3_32k_1})};
+
 class BluetoothLeAudioCodecsProviderTest
     : public ::testing::TestWithParam<OffloadSetting> {
  public:
@@ -227,6 +259,19 @@
     return le_audio_codec_capabilities;
   }
 
+  std::unordered_map<SessionType, std::vector<CodecInfo>>
+  RunCodecInfoTestCase() {
+    auto& [scenario_lists, configuration_lists, codec_configuration_lists,
+           strategy_configuration_lists] = GetParam();
+    LeAudioOffloadSetting le_audio_offload_setting(
+        scenario_lists, configuration_lists, codec_configuration_lists,
+        strategy_configuration_lists);
+    auto le_audio_codec_capabilities =
+        BluetoothLeAudioCodecsProvider::GetLeAudioCodecInfo(
+            std::make_optional(le_audio_offload_setting));
+    return le_audio_codec_capabilities;
+  }
+
  private:
   static inline OffloadSetting CreateTestCase(
       const ScenarioList& scenario_list,
@@ -392,6 +437,39 @@
   ASSERT_TRUE(!le_audio_codec_capabilities.empty());
 }
 
+class ComposeLeAudioAymmetricCodecInfoTest
+    : public BluetoothLeAudioCodecsProviderTest {
+ public:
+};
+
+TEST_P(ComposeLeAudioAymmetricCodecInfoTest, AsymmetricCodecInfoNotEmpty) {
+  Initialize();
+  auto le_audio_codec_info_map = RunCodecInfoTestCase();
+  ASSERT_TRUE(!le_audio_codec_info_map.empty());
+  // Check true asymmetric codec info
+  ASSERT_TRUE(!le_audio_codec_info_map
+                   [SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH]
+                       .empty());
+  ASSERT_TRUE(!le_audio_codec_info_map
+                   [SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH]
+                       .empty());
+  auto required_flag = ConfigurationFlags();
+  required_flag.bitmask |= ConfigurationFlags::ALLOW_ASYMMETRIC_CONFIGURATIONS;
+
+  auto codec_info = le_audio_codec_info_map
+      [SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH][0];
+  ASSERT_EQ(codec_info.transport.getTag(), CodecInfo::Transport::Tag::leAudio);
+  auto& transport =
+      codec_info.transport.get<CodecInfo::Transport::Tag::leAudio>();
+  ASSERT_EQ(transport.flags, std::make_optional(required_flag));
+
+  codec_info = le_audio_codec_info_map
+      [SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH][0];
+  ASSERT_EQ(codec_info.transport.getTag(), CodecInfo::Transport::Tag::leAudio);
+  transport = codec_info.transport.get<CodecInfo::Transport::Tag::leAudio>();
+  ASSERT_EQ(transport.flags, std::make_optional(required_flag));
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GetScenariosTest);
 INSTANTIATE_TEST_SUITE_P(
     BluetoothLeAudioCodecsProviderTest, GetScenariosTest,
@@ -434,6 +512,15 @@
         kValidScenarioList, kValidConfigurationList,
         kValidCodecConfigurationList, kValidStrategyConfigurationList)));
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+    ComposeLeAudioAymmetricCodecInfoTest);
+INSTANTIATE_TEST_SUITE_P(
+    BluetoothLeAudioCodecsProviderTest, ComposeLeAudioAymmetricCodecInfoTest,
+    ::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
+        kValidAsymmetricScenarioList, kValidAsymmetricConfigurationList,
+        kValidAsymmetricCodecConfigurationList,
+        kValidStrategyConfigurationList)));
+
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();
diff --git a/bluetooth/finder/aidl/vts/Android.bp b/bluetooth/finder/aidl/vts/Android.bp
index 49d2d45..29a0c0f 100644
--- a/bluetooth/finder/aidl/vts/Android.bp
+++ b/bluetooth/finder/aidl/vts/Android.bp
@@ -32,7 +32,6 @@
     ],
     static_libs: [
         "android.hardware.bluetooth.finder-V1-ndk",
-        "libbluetooth-types",
     ],
     test_config: "VtsHalBluetoothFinderTargetTest.xml",
     test_suites: [
diff --git a/bluetooth/ranging/aidl/vts/Android.bp b/bluetooth/ranging/aidl/vts/Android.bp
index bcae5d0..7f6ceff 100644
--- a/bluetooth/ranging/aidl/vts/Android.bp
+++ b/bluetooth/ranging/aidl/vts/Android.bp
@@ -18,7 +18,6 @@
     ],
     static_libs: [
         "android.hardware.bluetooth.ranging-V2-ndk",
-        "libbluetooth-types",
     ],
     test_config: "VtsHalBluetoothRangingTargetTest.xml",
     test_suites: [
diff --git a/bluetooth/socket/aidl/vts/Android.bp b/bluetooth/socket/aidl/vts/Android.bp
index 174dd4b..c1ba562 100644
--- a/bluetooth/socket/aidl/vts/Android.bp
+++ b/bluetooth/socket/aidl/vts/Android.bp
@@ -19,7 +19,6 @@
     static_libs: [
         "android.hardware.bluetooth.socket-V1-ndk",
         "android.hardware.contexthub-V4-ndk",
-        "libbluetooth-types",
     ],
     test_suites: [
         "general-tests",
diff --git a/boot/aidl/default/Android.bp b/boot/aidl/default/Android.bp
index c1d3c57..2fd2dad 100644
--- a/boot/aidl/default/Android.bp
+++ b/boot/aidl/default/Android.bp
@@ -57,7 +57,7 @@
     name: "android.hardware.boot-service.default_recovery",
     defaults: ["android.hardware.boot-service_common"],
     init_rc: ["android.hardware.boot-service.default_recovery.rc"],
-    vintf_fragments: ["android.hardware.boot-service.default.xml"],
+    vintf_fragment_modules: ["android.hardware.boot-service.default.xml.recovery"],
     recovery: true,
 
     shared_libs: [
@@ -77,11 +77,16 @@
     installable: false,
 }
 
-prebuilt_etc {
+vintf_fragment {
+    name: "android.hardware.boot-service.default.xml.recovery",
+    src: "android.hardware.boot-service.default.xml",
+    recovery: true,
+}
+
+vintf_fragment {
     name: "android.hardware.boot-service.default.xml",
     src: "android.hardware.boot-service.default.xml",
-    sub_dir: "vintf",
-    installable: false,
+    vendor: true,
 }
 
 apex {
@@ -98,6 +103,8 @@
     ],
     prebuilts: [
         "android.hardware.boot-service.default.rc",
+    ],
+    vintf_fragment_modules: [
         "android.hardware.boot-service.default.xml",
     ],
 }
diff --git a/boot/aidl/vts/functional/VtsHalBootAidlTargetTest.cpp b/boot/aidl/vts/functional/VtsHalBootAidlTargetTest.cpp
index 93c8376..228e2af 100644
--- a/boot/aidl/vts/functional/VtsHalBootAidlTargetTest.cpp
+++ b/boot/aidl/vts/functional/VtsHalBootAidlTargetTest.cpp
@@ -84,20 +84,20 @@
 
     for (int s = 0; s < 2; s++) {
         const auto result = boot->setActiveBootSlot(s);
-        ASSERT_TRUE(result.isOk());
+        ASSERT_TRUE(result.isOk()) << result;
     }
     {
         // Restore original flags to avoid problems on reboot
         auto result = boot->setActiveBootSlot(curSlot);
-        ASSERT_TRUE(result.isOk());
+        ASSERT_TRUE(result.isOk()) << result;
 
         if (!otherBootable) {
             const auto result = boot->setSlotAsUnbootable(otherSlot);
-            ASSERT_TRUE(result.isOk());
+            ASSERT_TRUE(result.isOk()) << result;
         }
 
         result = boot->markBootSuccessful();
-        ASSERT_TRUE(result.isOk());
+        ASSERT_TRUE(result.isOk()) << result;
     }
     {
         int slots = 0;
@@ -116,19 +116,19 @@
     boot->isSlotBootable(otherSlot, &otherBootable);
     {
         auto result = boot->setSlotAsUnbootable(otherSlot);
-        ASSERT_TRUE(result.isOk());
+        ASSERT_TRUE(result.isOk()) << result;
         boot->isSlotBootable(otherSlot, &otherBootable);
         ASSERT_FALSE(otherBootable);
 
         // Restore original flags to avoid problems on reboot
         if (otherBootable) {
             result = boot->setActiveBootSlot(otherSlot);
-            ASSERT_TRUE(result.isOk());
+            ASSERT_TRUE(result.isOk()) << result;
         }
         result = boot->setActiveBootSlot(curSlot);
-        ASSERT_TRUE(result.isOk());
+        ASSERT_TRUE(result.isOk()) << result;
         result = boot->markBootSuccessful();
-        ASSERT_TRUE(result.isOk());
+        ASSERT_TRUE(result.isOk()) << result;
     }
     {
         int32_t slots = 0;
@@ -143,7 +143,7 @@
     for (int s = 0; s < 2; s++) {
         bool bootable = false;
         const auto res = boot->isSlotBootable(s, &bootable);
-        ASSERT_TRUE(res.isOk()) << res.getMessage();
+        ASSERT_TRUE(res.isOk()) << res;
     }
     int32_t slots = 0;
     boot->getNumberSlots(&slots);
@@ -184,7 +184,7 @@
     {
         const string emptySuffix = "";
         const auto result = boot->getSuffix(numSlots, &suffixStr);
-        ASSERT_TRUE(result.isOk());
+        ASSERT_TRUE(result.isOk()) << result;
         ASSERT_EQ(suffixStr, emptySuffix);
     }
 }
diff --git a/broadcastradio/aidl/default/BroadcastRadio.cpp b/broadcastradio/aidl/default/BroadcastRadio.cpp
index 015cae0..f19a4e5 100644
--- a/broadcastradio/aidl/default/BroadcastRadio.cpp
+++ b/broadcastradio/aidl/default/BroadcastRadio.cpp
@@ -790,14 +790,12 @@
 ScopedAStatus BroadcastRadio::setParameters(
         [[maybe_unused]] const vector<VendorKeyValue>& parameters,
         vector<VendorKeyValue>* returnParameters) {
-    // TODO(b/243682330) Support vendor parameter functionality
     *returnParameters = {};
     return ScopedAStatus::ok();
 }
 
 ScopedAStatus BroadcastRadio::getParameters([[maybe_unused]] const vector<string>& keys,
                                             vector<VendorKeyValue>* returnParameters) {
-    // TODO(b/243682330) Support vendor parameter functionality
     *returnParameters = {};
     return ScopedAStatus::ok();
 }
diff --git a/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp b/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
index 7e9e458..508d8f1 100644
--- a/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
+++ b/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
@@ -767,6 +767,7 @@
     }
 
     // try tuning
+    mCallback->reset();
     auto result = mModule->tune(hdSel);
 
     // expect a failure if it's not supported
@@ -813,12 +814,13 @@
     }
 
     ProgramSelector sel = {};
-    uint64_t freq = 0;
+    uint64_t dabSidExt = 0;
     bool dabStationPresent = false;
     for (auto&& programInfo : *programList) {
         if (!utils::hasId(programInfo.selector, IdentifierType::DAB_FREQUENCY_KHZ)) {
             continue;
         }
+        uint64_t freq = 0;
         for (auto&& config_entry : config) {
             if (config_entry.frequencyKhz ==
                 utils::getId(programInfo.selector, IdentifierType::DAB_FREQUENCY_KHZ, 0)) {
@@ -831,7 +833,7 @@
         if (freq == 0) {
             continue;
         }
-        int64_t dabSidExt = utils::getId(programInfo.selector, IdentifierType::DAB_SID_EXT, 0);
+        dabSidExt = utils::getId(programInfo.selector, IdentifierType::DAB_SID_EXT, 0);
         int64_t dabEns = utils::getId(programInfo.selector, IdentifierType::DAB_ENSEMBLE, 0);
         sel = makeSelectorDab(dabSidExt, (int32_t)dabEns, freq);
         dabStationPresent = true;
@@ -843,7 +845,7 @@
     }
 
     // try tuning
-
+    mCallback->reset();
     auto result = mModule->tune(sel);
 
     // expect a failure if it's not supported
@@ -860,9 +862,9 @@
     LOG(DEBUG) << "Current program info: " << infoCb.toString();
 
     // it should tune exactly to what was requested
-    vector<int64_t> freqs = bcutils::getAllIds(infoCb.selector, IdentifierType::DAB_FREQUENCY_KHZ);
-    EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq))
-            << "DAB freq " << freq << " kHz is not sent back by callback.";
+    vector<int64_t> sidExts = bcutils::getAllIds(infoCb.selector, IdentifierType::DAB_SID_EXT);
+    EXPECT_NE(sidExts.end(), find(sidExts.begin(), sidExts.end(), dabSidExt))
+            << "DAB SID ext " << std::hex << dabSidExt << " is not sent back by callback.";
 }
 
 /**
diff --git a/camera/provider/aidl/vts/device_cb.cpp b/camera/provider/aidl/vts/device_cb.cpp
index bfd1cd1..940b4af 100644
--- a/camera/provider/aidl/vts/device_cb.cpp
+++ b/camera/provider/aidl/vts/device_cb.cpp
@@ -421,12 +421,19 @@
     }
 
     for (const auto& buffer : results.outputBuffers) {
+        std::unique_lock<std::mutex> l(mLock);
         CameraAidlTest::InFlightRequest::StreamBufferAndTimestamp streamBufferAndTimestamp;
-        auto outstandingBuffers = mUseHalBufManager ? mOutstandingBufferIds :
+        auto& outstandingBuffers = mUseHalBufManager ? mOutstandingBufferIds :
             request->mOutstandingBufferIds;
         auto bufferId = mUseHalBufManager ? buffer.bufferId : results.frameNumber;
-        auto outputBuffer = outstandingBuffers.empty() ? ::android::makeFromAidl(buffer.buffer) :
-            outstandingBuffers[buffer.streamId][bufferId];
+        const native_handle_t *outputBuffer = nullptr;
+        if (outstandingBuffers.empty()) {
+           outputBuffer = ::android::makeFromAidl(buffer.buffer);
+        } else if (outstandingBuffers[buffer.streamId].contains(bufferId)) {
+            outputBuffer = outstandingBuffers[buffer.streamId][bufferId];
+        } else {
+            ALOGV("%s: Invalid bufferId: %" PRId64, __FUNCTION__, bufferId);
+        }
         streamBufferAndTimestamp.buffer = {buffer.streamId,
                                            bufferId,
                                            outputBuffer,
diff --git a/compatibility_matrices/Android.bp b/compatibility_matrices/Android.bp
index 825c931..19f4839 100644
--- a/compatibility_matrices/Android.bp
+++ b/compatibility_matrices/Android.bp
@@ -147,6 +147,6 @@
     stem: "compatibility_matrix.202504.xml",
     srcs: ["compatibility_matrix.202504.xml"],
     kernel_configs: [
-        "kernel_config_w_6.12",
+        "kernel_config_b_6.12",
     ],
 }
diff --git a/compatibility_matrices/bump.py b/compatibility_matrices/bump.py
index ee2fa88..bcb0fa6 100755
--- a/compatibility_matrices/bump.py
+++ b/compatibility_matrices/bump.py
@@ -181,14 +181,14 @@
                         help="VINTF level of the next version (e.g. 202504)")
     parser.add_argument("current_letter",
                         type=str,
-                        help="Letter of the API level of the current version (e.g. v)")
+                        help="Letter of the API level of the current version (e.g. b)")
     parser.add_argument("next_letter",
                         type=str,
-                        help="Letter of the API level of the next version (e.g. w)")
+                        help="Letter of the API level of the next version (e.g. c)")
     parser.add_argument("platform_version",
                         type=str,
                         nargs="?",
-                        help="Android release version number number (e.g. 15)")
+                        help="Android release version number number (e.g. 16)")
     cmdline_args = parser.parse_args()
 
     Bump(cmdline_args).run()
diff --git a/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl
index 2646d15..2940745 100644
--- a/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl
+++ b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl
@@ -51,14 +51,6 @@
   void sendMessageDeliveryStatusToHub(in int contextHubId, in android.hardware.contexthub.MessageDeliveryStatus messageDeliveryStatus);
   List<android.hardware.contexthub.HubInfo> getHubs();
   List<android.hardware.contexthub.EndpointInfo> getEndpoints();
-  void registerEndpoint(in android.hardware.contexthub.EndpointInfo endpoint);
-  void unregisterEndpoint(in android.hardware.contexthub.EndpointInfo endpoint);
-  void registerEndpointCallback(in android.hardware.contexthub.IEndpointCallback callback);
-  int[2] requestSessionIdRange(int size);
-  void openEndpointSession(int sessionId, in android.hardware.contexthub.EndpointId destination, in android.hardware.contexthub.EndpointId initiator, in @nullable String serviceDescriptor);
-  void sendMessageToEndpoint(int sessionId, in android.hardware.contexthub.Message msg);
-  void sendMessageDeliveryStatusToEndpoint(int sessionId, in android.hardware.contexthub.MessageDeliveryStatus msgStatus);
-  void closeEndpointSession(int sessionId, in android.hardware.contexthub.Reason reason);
-  void endpointSessionOpenComplete(int sessionId);
+  @PropagateAllowBlocking android.hardware.contexthub.IEndpointCommunication registerEndpointHub(in android.hardware.contexthub.IEndpointCallback callback, in android.hardware.contexthub.HubInfo hubInfo);
   const int EX_CONTEXT_HUB_UNSPECIFIED = (-1) /* -1 */;
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IEndpointCommunication.aidl
similarity index 66%
copy from wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl
copy to contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IEndpointCommunication.aidl
index 0600def..8742415 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl
+++ b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IEndpointCommunication.aidl
@@ -31,11 +31,16 @@
 // 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.wifi.supplicant;
-@Backing(type="int") @VintfStability
-enum UsdReasonCode {
-  FAILURE_UNKNOWN = 0,
-  TIMEOUT = 1,
-  USER_REQUESTED = 2,
-  INVALID_ARGS = 3,
+package android.hardware.contexthub;
+@VintfStability
+interface IEndpointCommunication {
+  void registerEndpoint(in android.hardware.contexthub.EndpointInfo endpoint);
+  void unregisterEndpoint(in android.hardware.contexthub.EndpointInfo endpoint);
+  int[2] requestSessionIdRange(int size);
+  void openEndpointSession(int sessionId, in android.hardware.contexthub.EndpointId destination, in android.hardware.contexthub.EndpointId initiator, in @nullable String serviceDescriptor);
+  void sendMessageToEndpoint(int sessionId, in android.hardware.contexthub.Message msg);
+  void sendMessageDeliveryStatusToEndpoint(int sessionId, in android.hardware.contexthub.MessageDeliveryStatus msgStatus);
+  void closeEndpointSession(int sessionId, in android.hardware.contexthub.Reason reason);
+  void endpointSessionOpenComplete(int sessionId);
+  void unregister();
 }
diff --git a/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl b/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl
index eb6d051..3fb452c 100644
--- a/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl
+++ b/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl
@@ -18,19 +18,16 @@
 
 import android.hardware.contexthub.ContextHubInfo;
 import android.hardware.contexthub.ContextHubMessage;
-import android.hardware.contexthub.EndpointId;
 import android.hardware.contexthub.EndpointInfo;
 import android.hardware.contexthub.HostEndpointInfo;
 import android.hardware.contexthub.HubInfo;
 import android.hardware.contexthub.IContextHubCallback;
 import android.hardware.contexthub.IEndpointCallback;
-import android.hardware.contexthub.Message;
+import android.hardware.contexthub.IEndpointCommunication;
 import android.hardware.contexthub.MessageDeliveryStatus;
 import android.hardware.contexthub.NanSessionStateUpdate;
 import android.hardware.contexthub.NanoappBinary;
 import android.hardware.contexthub.NanoappInfo;
-import android.hardware.contexthub.Reason;
-import android.hardware.contexthub.Service;
 import android.hardware.contexthub.Setting;
 
 @VintfStability
@@ -271,125 +268,22 @@
     List<EndpointInfo> getEndpoints();
 
     /**
-     * Publishes an endpoint from the calling side (e.g. Android). Endpoints must be registered
-     * prior to starting a session.
+     * Registers a new hub for endpoint communication which will receive events for its endpoints
+     * over the given callback. Returns an interface for the hub to register endpoints, start
+     * sessions, and send messages.
+     *
+     * It is valid for the same callback to be registered for multiple hubs, as the
+     * IEndpointCallback events provide sufficient information to determine which hub the event is
+     * intended for:
+     * * session ids are allocated to a specific hub and are unique
+     * * endpoints are identified by hub and endpoint id
+     *
+     * @param callback Interface to send endpoint events targeting the caller
+     * @param hubInfo Details of the hub being registered
+     * @return Interface for the hub to interact with other endpoint hubs
+     *
+     * @throws EX_ILLEGAL_STATE if hubInfo.hubId has already been registered
      */
-    void registerEndpoint(in EndpointInfo endpoint);
-
-    /**
-     * Teardown an endpoint from the calling side (e.g. Android). This endpoint must have already
-     * been published via registerEndpoint().
-     */
-    void unregisterEndpoint(in EndpointInfo endpoint);
-
-    /**
-     * Attaches a callback interface to receive events targeted at endpoints registered by the
-     * caller.
-     */
-    void registerEndpointCallback(in IEndpointCallback callback);
-
-    /**
-     * Request a range of session IDs for the caller to use when initiating sessions. This may be
-     * called more than once, but typical usage is to request a large enough range to accommodate
-     * the maximum expected number of concurrent sessions, but not overly large as to limit other
-     * clients.
-     *
-     * @param size The number of sessionId reserved for host-initiated sessions. This number should
-     *         be less than or equal to 1024.
-     *
-     * @return An array with two elements representing the smallest and largest possible session id
-     *         available for host.
-     *
-     * @throws EX_ILLEGAL_ARGUMENT if the size is invalid.
-     * @throws EX_SERVICE_SPECIFIC if the id range requested cannot be allocated.
-     */
-    int[2] requestSessionIdRange(int size);
-
-    /**
-     * Request to open a session for communication between an endpoint previously registered by the
-     * caller and a target endpoint found in getEndpoints(), optionally scoped to a service
-     * published by the target endpoint.
-     *
-     * Upon returning from this function, the session is in pending state, and the final result will
-     * be given by an asynchronous call to onEndpointSessionOpenComplete() on success, or
-     * onCloseEndpointSession() on failure.
-     *
-     * @param sessionId Caller-allocated session identifier, which must be unique across all active
-     *         sessions, and must fall in a range allocated via requestSessionIdRange().
-     * @param destination The EndpointId representing the destination side of the session.
-     * @param initiator The EndpointId representing the initiating side of the session, which
-     *         must've already been published through registerEndpoint().
-     * @param serviceDescriptor Descriptor for the service specification for scoping this session
-     *         (nullable). Null indicates a fully custom marshalling scheme. The value should match
-     *         a published descriptor for both destination and initiator.
-     *
-     * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
-     *         arguments is invalid.
-     * @throws EX_SERVICE_SPECIFIC on other errors
-     *         - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
-     */
-    void openEndpointSession(int sessionId, in EndpointId destination, in EndpointId initiator,
-            in @nullable String serviceDescriptor);
-
-    /**
-     * Send a message from one endpoint to another on the (currently open) session.
-     *
-     * @param sessionId The integer representing the communication session, previously set in
-     *         openEndpointSession() or onEndpointSessionOpenRequest().
-     * @param msg The Message object representing a message to endpoint from the endpoint on host.
-     *
-     * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
-     *         arguments is invalid.
-     * @throws EX_SERVICE_SPECIFIC on other errors
-     *         - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
-     */
-    void sendMessageToEndpoint(int sessionId, in Message msg);
-
-    /**
-     * Sends a message delivery status to the endpoint in response to receiving a Message with flag
-     * FLAG_REQUIRES_DELIVERY_STATUS. Each message with the flag should have a MessageDeliveryStatus
-     * response. This method sends the message delivery status back to the remote endpoint for a
-     * session.
-     *
-     * @param sessionId The integer representing the communication session, previously set in
-     *         openEndpointSession() or onEndpointSessionOpenRequest().
-     * @param msgStatus The MessageDeliveryStatus object representing the delivery status for a
-     *         specific message (identified by the sequenceNumber) within the session.
-     *
-     * @throws EX_UNSUPPORTED_OPERATION if ContextHubInfo.supportsReliableMessages is false for
-     *          the hub involved in this session.
-     */
-    void sendMessageDeliveryStatusToEndpoint(int sessionId, in MessageDeliveryStatus msgStatus);
-
-    /**
-     * Closes a session previously opened by openEndpointSession() or requested via
-     * onEndpointSessionOpenRequest(). Processing of session closure must be ordered/synchronized
-     * with message delivery, such that if this session was open, any messages previously passed to
-     * sendMessageToEndpoint() that are still in-flight must still be delivered before the session
-     * is closed. Any in-flight messages to the endpoint that requested to close the session will
-     * not be delivered.
-     *
-     * @param sessionId The integer representing the communication session, previously set in
-     *         openEndpointSession() or onEndpointSessionOpenRequest().
-     * @param reason The reason for this close endpoint session request.
-     *
-     * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
-     *         arguments is invalid.
-     * @throws EX_SERVICE_SPECIFIC on other errors
-     *         - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
-     */
-    void closeEndpointSession(int sessionId, in Reason reason);
-
-    /**
-     * Notifies the HAL that the session requested by onEndpointSessionOpenRequest is ready to use.
-     *
-     * @param sessionId The integer representing the communication session, previously set in
-     *         onEndpointSessionOpenRequest(). This id is assigned by the HAL.
-     *
-     * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
-     *         arguments is invalid.
-     * @throws EX_SERVICE_SPECIFIC on other errors
-     *         - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
-     */
-    void endpointSessionOpenComplete(int sessionId);
+    @PropagateAllowBlocking
+    IEndpointCommunication registerEndpointHub(in IEndpointCallback callback, in HubInfo hubInfo);
 }
diff --git a/contexthub/aidl/android/hardware/contexthub/IEndpointCommunication.aidl b/contexthub/aidl/android/hardware/contexthub/IEndpointCommunication.aidl
new file mode 100644
index 0000000..e5045ba
--- /dev/null
+++ b/contexthub/aidl/android/hardware/contexthub/IEndpointCommunication.aidl
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.contexthub;
+
+import android.hardware.contexthub.EndpointId;
+import android.hardware.contexthub.EndpointInfo;
+import android.hardware.contexthub.IEndpointCallback;
+import android.hardware.contexthub.Message;
+import android.hardware.contexthub.MessageDeliveryStatus;
+import android.hardware.contexthub.Reason;
+import android.hardware.contexthub.Service;
+
+@VintfStability
+interface IEndpointCommunication {
+    /**
+     * Publishes an endpoint from the calling side (e.g. Android). Endpoints must be registered
+     * prior to starting a session.
+     */
+    void registerEndpoint(in EndpointInfo endpoint);
+
+    /**
+     * Teardown an endpoint from the calling side (e.g. Android). This endpoint must have already
+     * been published via registerEndpoint().
+     */
+    void unregisterEndpoint(in EndpointInfo endpoint);
+
+    /**
+     * Request a range of session IDs for the caller to use when initiating sessions. This may be
+     * called more than once, but typical usage is to request a large enough range to accommodate
+     * the maximum expected number of concurrent sessions, but not overly large as to limit other
+     * clients.
+     *
+     * @param size The number of sessionId reserved for host-initiated sessions. This number should
+     *         be less than or equal to 1024.
+     *
+     * @return An array with two elements representing the smallest and largest possible session id
+     *         available for host.
+     *
+     * @throws EX_ILLEGAL_ARGUMENT if the size is invalid.
+     * @throws EX_SERVICE_SPECIFIC if the id range requested cannot be allocated.
+     */
+    int[2] requestSessionIdRange(int size);
+
+    /**
+     * Request to open a session for communication between an endpoint previously registered by the
+     * caller and a target endpoint found in getEndpoints(), optionally scoped to a service
+     * published by the target endpoint.
+     *
+     * Upon returning from this function, the session is in pending state, and the final result will
+     * be given by an asynchronous call to onEndpointSessionOpenComplete() on success, or
+     * onCloseEndpointSession() on failure.
+     *
+     * @param sessionId Caller-allocated session identifier, which must be unique across all active
+     *         sessions, and must fall in a range allocated via requestSessionIdRange().
+     * @param destination The EndpointId representing the destination side of the session.
+     * @param initiator The EndpointId representing the initiating side of the session, which
+     *         must've already been published through registerEndpoint().
+     * @param serviceDescriptor Descriptor for the service specification for scoping this session
+     *         (nullable). Null indicates a fully custom marshalling scheme. The value should match
+     *         a published descriptor for both destination and initiator.
+     *
+     * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
+     *         arguments is invalid.
+     * @throws EX_SERVICE_SPECIFIC on other errors
+     *         - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
+     */
+    void openEndpointSession(int sessionId, in EndpointId destination, in EndpointId initiator,
+            in @nullable String serviceDescriptor);
+
+    /**
+     * Send a message from one endpoint to another on the (currently open) session.
+     *
+     * @param sessionId The integer representing the communication session, previously set in
+     *         openEndpointSession() or onEndpointSessionOpenRequest().
+     * @param msg The Message object representing a message to endpoint from the endpoint on host.
+     *
+     * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
+     *         arguments is invalid.
+     * @throws EX_SERVICE_SPECIFIC on other errors
+     *         - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
+     */
+    void sendMessageToEndpoint(int sessionId, in Message msg);
+
+    /**
+     * Sends a message delivery status to the endpoint in response to receiving a Message with flag
+     * FLAG_REQUIRES_DELIVERY_STATUS. Each message with the flag should have a MessageDeliveryStatus
+     * response. This method sends the message delivery status back to the remote endpoint for a
+     * session.
+     *
+     * @param sessionId The integer representing the communication session, previously set in
+     *         openEndpointSession() or onEndpointSessionOpenRequest().
+     * @param msgStatus The MessageDeliveryStatus object representing the delivery status for a
+     *         specific message (identified by the sequenceNumber) within the session.
+     *
+     * @throws EX_UNSUPPORTED_OPERATION if ContextHubInfo.supportsReliableMessages is false for
+     *          the hub involved in this session.
+     */
+    void sendMessageDeliveryStatusToEndpoint(int sessionId, in MessageDeliveryStatus msgStatus);
+
+    /**
+     * Closes a session previously opened by openEndpointSession() or requested via
+     * onEndpointSessionOpenRequest(). Processing of session closure must be ordered/synchronized
+     * with message delivery, such that if this session was open, any messages previously passed to
+     * sendMessageToEndpoint() that are still in-flight must still be delivered before the session
+     * is closed. Any in-flight messages to the endpoint that requested to close the session will
+     * not be delivered.
+     *
+     * @param sessionId The integer representing the communication session, previously set in
+     *         openEndpointSession() or onEndpointSessionOpenRequest().
+     * @param reason The reason for this close endpoint session request.
+     *
+     * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
+     *         arguments is invalid.
+     * @throws EX_SERVICE_SPECIFIC on other errors
+     *         - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
+     */
+    void closeEndpointSession(int sessionId, in Reason reason);
+
+    /**
+     * Notifies the HAL that the session requested by onEndpointSessionOpenRequest is ready to use.
+     *
+     * @param sessionId The integer representing the communication session, previously set in
+     *         onEndpointSessionOpenRequest(). This id is assigned by the HAL.
+     *
+     * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
+     *         arguments is invalid.
+     * @throws EX_SERVICE_SPECIFIC on other errors
+     *         - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
+     */
+    void endpointSessionOpenComplete(int sessionId);
+
+    /**
+     * Unregisters this hub. Subsequent calls on this interface will fail.
+     *
+     * @throws EX_ILLEGAL_STATE if this interface was already unregistered.
+     */
+    void unregister();
+}
diff --git a/contexthub/aidl/android/hardware/contexthub/Service.aidl b/contexthub/aidl/android/hardware/contexthub/Service.aidl
index fd748c3..e107193 100644
--- a/contexthub/aidl/android/hardware/contexthub/Service.aidl
+++ b/contexthub/aidl/android/hardware/contexthub/Service.aidl
@@ -16,6 +16,17 @@
 
 package android.hardware.contexthub;
 
+/**
+ * Services that are provided by an endpoint.
+ *
+ * To support testing, the following service is defined here:
+ *  1. Test echo service:
+ *     - This service responds to a received message back to the sender with a
+ *       message with identical content as the received message.
+ *     - Format:                Service::RpcFormat::CUSTOM
+ *     - Service descriptor:    android.hardware.contexthub.test.EchoService
+ *     - Major version:         1
+ */
 @VintfStability
 parcelable Service {
     /**
diff --git a/contexthub/aidl/default/Android.bp b/contexthub/aidl/default/Android.bp
index c0b147c..d4d03e4 100644
--- a/contexthub/aidl/default/Android.bp
+++ b/contexthub/aidl/default/Android.bp
@@ -89,5 +89,6 @@
     prebuilts: [
         "android.hardware.contexthub-service.example.rc",
         "contexthub-default.xml",
+        "android.hardware.context_hub.prebuilt.xml",
     ],
 }
diff --git a/contexthub/aidl/default/ContextHub.cpp b/contexthub/aidl/default/ContextHub.cpp
index c1af0a3..433617e 100644
--- a/contexthub/aidl/default/ContextHub.cpp
+++ b/contexthub/aidl/default/ContextHub.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "contexthub-impl/ContextHub.h"
+#include "aidl/android/hardware/contexthub/IContextHubCallback.h"
 
 #ifndef LOG_TAG
 #define LOG_TAG "CHRE"
@@ -22,6 +23,8 @@
 
 #include <inttypes.h>
 #include <log/log.h>
+#include <optional>
+#include <thread>
 
 using ::ndk::ScopedAStatus;
 
@@ -62,23 +65,12 @@
         },
 };
 
+//! Mutex used to ensure callbacks are called after the initial function returns.
+std::mutex gCallbackMutex;
+
 }  // anonymous namespace
 
-ScopedAStatus ContextHub::getContextHubs(std::vector<ContextHubInfo>* out_contextHubInfos) {
-    ContextHubInfo hub = {};
-    hub.name = "Mock Context Hub";
-    hub.vendor = "AOSP";
-    hub.toolchain = "n/a";
-    hub.id = kMockHubId;
-    hub.peakMips = 1;
-    hub.maxSupportedMessageLengthBytes = 4096;
-    hub.chrePlatformId = UINT64_C(0x476f6f6754000000);
-    hub.chreApiMajorVersion = 1;
-    hub.chreApiMinorVersion = 6;
-    hub.supportsReliableMessages = false;
-
-    out_contextHubInfos->push_back(hub);
-
+ScopedAStatus ContextHub::getContextHubs(std::vector<ContextHubInfo>* /* out_contextHubInfos */) {
     return ScopedAStatus::ok();
 }
 
@@ -158,10 +150,11 @@
 
 ScopedAStatus ContextHub::setTestMode(bool enable) {
     if (enable) {
-        std::unique_lock<std::mutex> lock(mEndpointMutex);
-        mEndpoints.clear();
-        mEndpointSessions.clear();
-        mEndpointCallback = nullptr;
+        std::lock_guard lock(mHostHubsLock);
+        for (auto& [id, hub] : mIdToHostHub) {
+            hub->mActive = false;
+        }
+        mIdToHostHub.clear();
     }
     return ScopedAStatus::ok();
 }
@@ -191,43 +184,26 @@
         return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
 
-    ContextHubInfo hub = {};
-    hub.name = "Mock Context Hub";
-    hub.vendor = "AOSP";
-    hub.toolchain = "n/a";
-    hub.id = kMockHubId;
-    hub.peakMips = 1;
-    hub.maxSupportedMessageLengthBytes = 4096;
-    hub.chrePlatformId = UINT64_C(0x476f6f6754000000);
-    hub.chreApiMajorVersion = 1;
-    hub.chreApiMinorVersion = 6;
-    hub.supportsReliableMessages = false;
-
-    HubInfo hubInfo1 = {};
-    hubInfo1.hubId = hub.chrePlatformId;
-    hubInfo1.hubDetails = HubInfo::HubDetails::make<HubInfo::HubDetails::Tag::contextHubInfo>(hub);
-
     VendorHubInfo vendorHub = {};
     vendorHub.name = "Mock Vendor Hub";
     vendorHub.version = 42;
 
-    HubInfo hubInfo2 = {};
-    hubInfo2.hubId = kMockVendorHubId;
-    hubInfo2.hubDetails =
+    HubInfo hubInfo1 = {};
+    hubInfo1.hubId = kMockVendorHubId;
+    hubInfo1.hubDetails =
             HubInfo::HubDetails::make<HubInfo::HubDetails::Tag::vendorHubInfo>(vendorHub);
 
     VendorHubInfo vendorHub2 = {};
     vendorHub2.name = "Mock Vendor Hub 2";
     vendorHub2.version = 24;
 
-    HubInfo hubInfo3 = {};
-    hubInfo3.hubId = kMockVendorHub2Id;
-    hubInfo3.hubDetails =
+    HubInfo hubInfo2 = {};
+    hubInfo2.hubId = kMockVendorHub2Id;
+    hubInfo2.hubDetails =
             HubInfo::HubDetails::make<HubInfo::HubDetails::Tag::vendorHubInfo>(vendorHub2);
 
     _aidl_return->push_back(hubInfo1);
     _aidl_return->push_back(hubInfo2);
-    _aidl_return->push_back(hubInfo3);
 
     return ScopedAStatus::ok();
 };
@@ -239,7 +215,7 @@
 
     Service echoService;
     echoService.format = Service::RpcFormat::CUSTOM;
-    echoService.serviceDescriptor = "ECHO";
+    echoService.serviceDescriptor = "android.hardware.contexthub.test.EchoService";
     echoService.majorVersion = 1;
     echoService.minorVersion = 0;
 
@@ -252,7 +228,23 @@
     return ScopedAStatus::ok();
 };
 
-ScopedAStatus ContextHub::registerEndpoint(const EndpointInfo& in_endpoint) {
+ScopedAStatus ContextHub::registerEndpointHub(
+        const std::shared_ptr<IEndpointCallback>& in_callback, const HubInfo& in_hubInfo,
+        std::shared_ptr<IEndpointCommunication>* _aidl_return) {
+    std::lock_guard lock(mHostHubsLock);
+    if (mIdToHostHub.count(in_hubInfo.hubId)) {
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    auto hub = ndk::SharedRefBase::make<HubInterface>(*this, in_callback, in_hubInfo);
+    mIdToHostHub.insert({in_hubInfo.hubId, hub});
+    *_aidl_return = std::move(hub);
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus ContextHub::HubInterface::registerEndpoint(const EndpointInfo& in_endpoint) {
+    if (!mActive) {
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
     std::unique_lock<std::mutex> lock(mEndpointMutex);
 
     for (const EndpointInfo& endpoint : mEndpoints) {
@@ -265,7 +257,10 @@
     return ScopedAStatus::ok();
 };
 
-ScopedAStatus ContextHub::unregisterEndpoint(const EndpointInfo& in_endpoint) {
+ScopedAStatus ContextHub::HubInterface::unregisterEndpoint(const EndpointInfo& in_endpoint) {
+    if (!mActive) {
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
     std::unique_lock<std::mutex> lock(mEndpointMutex);
 
     for (auto it = mEndpoints.begin(); it != mEndpoints.end(); ++it) {
@@ -277,41 +272,47 @@
     return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
 };
 
-ScopedAStatus ContextHub::registerEndpointCallback(
-        const std::shared_ptr<IEndpointCallback>& in_callback) {
-    std::unique_lock<std::mutex> lock(mEndpointMutex);
-
-    mEndpointCallback = in_callback;
-    return ScopedAStatus::ok();
-};
-
-ScopedAStatus ContextHub::requestSessionIdRange(int32_t in_size,
-                                                std::array<int32_t, 2>* _aidl_return) {
+ScopedAStatus ContextHub::HubInterface::requestSessionIdRange(
+        int32_t in_size, std::array<int32_t, 2>* _aidl_return) {
+    if (!mActive) {
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
     constexpr int32_t kMaxSize = 1024;
     if (in_size > kMaxSize || _aidl_return == nullptr) {
         return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
 
+    uint16_t base = 0;
     {
-        std::lock_guard<std::mutex> lock(mEndpointMutex);
-        mMaxValidSessionId = in_size;
+        std::lock_guard lock(mHal.mHostHubsLock);
+        if (static_cast<int32_t>(USHRT_MAX) - mHal.mNextSessionIdBase + 1 < in_size) {
+            return ScopedAStatus::fromServiceSpecificError(EX_CONTEXT_HUB_UNSPECIFIED);
+        }
+        base = mHal.mNextSessionIdBase;
+        mHal.mNextSessionIdBase += in_size;
     }
 
-    _aidl_return->at(0) = 0;
-    _aidl_return->at(1) = in_size;
+    {
+        std::lock_guard<std::mutex> lock(mEndpointMutex);
+        (*_aidl_return)[0] = mBaseSessionId = base;
+        (*_aidl_return)[1] = mMaxSessionId = base + (in_size - 1);
+    }
     return ScopedAStatus::ok();
 };
 
-ScopedAStatus ContextHub::openEndpointSession(
+ScopedAStatus ContextHub::HubInterface::openEndpointSession(
         int32_t in_sessionId, const EndpointId& in_destination, const EndpointId& in_initiator,
         const std::optional<std::string>& in_serviceDescriptor) {
+    if (!mActive) {
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
     // We are not calling onCloseEndpointSession on failure because the remote endpoints (our
     // mock endpoints) always accept the session.
 
-    std::shared_ptr<IEndpointCallback> callback = nullptr;
+    std::weak_ptr<IEndpointCallback> callback;
     {
         std::unique_lock<std::mutex> lock(mEndpointMutex);
-        if (in_sessionId > mMaxValidSessionId) {
+        if (in_sessionId < mBaseSessionId || in_sessionId > mMaxSessionId) {
             ALOGE("openEndpointSession: session ID %" PRId32 " is invalid", in_sessionId);
             return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
         }
@@ -355,23 +356,31 @@
                 .serviceDescriptor = in_serviceDescriptor,
         });
 
-        if (mEndpointCallback != nullptr) {
-            callback = mEndpointCallback;
+        if (mEndpointCallback == nullptr) {
+            return ScopedAStatus::ok();
         }
+        callback = mEndpointCallback;
     }
 
-    if (callback != nullptr) {
-        callback->onEndpointSessionOpenComplete(in_sessionId);
-    }
+    std::unique_lock<std::mutex> lock(gCallbackMutex);
+    std::thread{[callback, in_sessionId]() {
+        std::unique_lock<std::mutex> lock(gCallbackMutex);
+        if (auto cb = callback.lock(); cb != nullptr) {
+            cb->onEndpointSessionOpenComplete(in_sessionId);
+        }
+    }}.detach();
     return ScopedAStatus::ok();
 };
 
-ScopedAStatus ContextHub::sendMessageToEndpoint(int32_t in_sessionId, const Message& in_msg) {
-    bool foundSession = false;
-    std::shared_ptr<IEndpointCallback> callback = nullptr;
+ScopedAStatus ContextHub::HubInterface::sendMessageToEndpoint(int32_t in_sessionId,
+                                                              const Message& in_msg) {
+    if (!mActive) {
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    std::weak_ptr<IEndpointCallback> callback;
     {
         std::unique_lock<std::mutex> lock(mEndpointMutex);
-
+        bool foundSession = false;
         for (const EndpointSession& session : mEndpointSessions) {
             if (session.sessionId == in_sessionId) {
                 foundSession = true;
@@ -379,36 +388,54 @@
             }
         }
 
-        if (mEndpointCallback != nullptr) {
-            callback = mEndpointCallback;
-        }
-    }
-
-    if (!foundSession) {
-        ALOGE("sendMessageToEndpoint: session ID %" PRId32 " is invalid", in_sessionId);
-        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    }
-
-    if (callback != nullptr) {
-        if (in_msg.flags & Message::FLAG_REQUIRES_DELIVERY_STATUS) {
-            MessageDeliveryStatus msgStatus = {};
-            msgStatus.messageSequenceNumber = in_msg.sequenceNumber;
-            msgStatus.errorCode = ErrorCode::OK;
-            callback->onMessageDeliveryStatusReceived(in_sessionId, msgStatus);
+        if (!foundSession) {
+            ALOGE("sendMessageToEndpoint: session ID %" PRId32 " is invalid", in_sessionId);
+            return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
         }
 
-        // Echo the message back
-        callback->onMessageReceived(in_sessionId, in_msg);
+        if (mEndpointCallback == nullptr) {
+            return ScopedAStatus::ok();
+        }
+        callback = mEndpointCallback;
     }
+
+    std::unique_lock<std::mutex> lock(gCallbackMutex);
+    if ((in_msg.flags & Message::FLAG_REQUIRES_DELIVERY_STATUS) != 0) {
+        MessageDeliveryStatus msgStatus = {};
+        msgStatus.messageSequenceNumber = in_msg.sequenceNumber;
+        msgStatus.errorCode = ErrorCode::OK;
+
+        std::thread{[callback, in_sessionId, msgStatus]() {
+            std::unique_lock<std::mutex> lock(gCallbackMutex);
+            if (auto cb = callback.lock(); cb != nullptr) {
+                cb->onMessageDeliveryStatusReceived(in_sessionId, msgStatus);
+            }
+        }}.detach();
+    }
+
+    // Echo the message back
+    std::thread{[callback, in_sessionId, in_msg]() {
+        std::unique_lock<std::mutex> lock(gCallbackMutex);
+        if (auto cb = callback.lock(); cb != nullptr) {
+            cb->onMessageReceived(in_sessionId, in_msg);
+        }
+    }}.detach();
     return ScopedAStatus::ok();
 };
 
-ScopedAStatus ContextHub::sendMessageDeliveryStatusToEndpoint(
+ScopedAStatus ContextHub::HubInterface::sendMessageDeliveryStatusToEndpoint(
         int32_t /* in_sessionId */, const MessageDeliveryStatus& /* in_msgStatus */) {
+    if (!mActive) {
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
     return ScopedAStatus::ok();
 };
 
-ScopedAStatus ContextHub::closeEndpointSession(int32_t in_sessionId, Reason /* in_reason */) {
+ScopedAStatus ContextHub::HubInterface::closeEndpointSession(int32_t in_sessionId,
+                                                             Reason /* in_reason */) {
+    if (!mActive) {
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
     std::unique_lock<std::mutex> lock(mEndpointMutex);
 
     for (auto it = mEndpointSessions.begin(); it != mEndpointSessions.end(); ++it) {
@@ -421,8 +448,20 @@
     return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
 };
 
-ScopedAStatus ContextHub::endpointSessionOpenComplete(int32_t /* in_sessionId */) {
+ScopedAStatus ContextHub::HubInterface::endpointSessionOpenComplete(int32_t /* in_sessionId */) {
+    if (!mActive) {
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
     return ScopedAStatus::ok();
 };
 
+ScopedAStatus ContextHub::HubInterface::unregister() {
+    if (!mActive.exchange(false)) {
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    std::lock_guard lock(mHal.mHostHubsLock);
+    mHal.mIdToHostHub.erase(kInfo.hubId);
+    return ScopedAStatus::ok();
+}
+
 }  // namespace aidl::android::hardware::contexthub
diff --git a/contexthub/aidl/default/include/contexthub-impl/ContextHub.h b/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
index 6da8bf2..65e84bb 100644
--- a/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
+++ b/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
@@ -17,7 +17,9 @@
 #pragma once
 
 #include <aidl/android/hardware/contexthub/BnContextHub.h>
+#include <aidl/android/hardware/contexthub/BnEndpointCommunication.h>
 
+#include <atomic>
 #include <mutex>
 #include <unordered_set>
 #include <vector>
@@ -56,54 +58,79 @@
 
     ::ndk::ScopedAStatus getHubs(std::vector<HubInfo>* _aidl_return) override;
     ::ndk::ScopedAStatus getEndpoints(std::vector<EndpointInfo>* _aidl_return) override;
-    ::ndk::ScopedAStatus registerEndpoint(const EndpointInfo& in_endpoint) override;
-    ::ndk::ScopedAStatus unregisterEndpoint(const EndpointInfo& in_endpoint) override;
-    ::ndk::ScopedAStatus registerEndpointCallback(
-            const std::shared_ptr<IEndpointCallback>& in_callback) override;
-    ::ndk::ScopedAStatus requestSessionIdRange(int32_t in_size,
-                                               std::array<int32_t, 2>* _aidl_return) override;
-    ::ndk::ScopedAStatus openEndpointSession(
-            int32_t in_sessionId, const EndpointId& in_destination, const EndpointId& in_initiator,
-            const std::optional<std::string>& in_serviceDescriptor) override;
-    ::ndk::ScopedAStatus sendMessageToEndpoint(int32_t in_sessionId,
-                                               const Message& in_msg) override;
-    ::ndk::ScopedAStatus sendMessageDeliveryStatusToEndpoint(
-            int32_t in_sessionId, const MessageDeliveryStatus& in_msgStatus) override;
-    ::ndk::ScopedAStatus closeEndpointSession(int32_t in_sessionId, Reason in_reason) override;
-    ::ndk::ScopedAStatus endpointSessionOpenComplete(int32_t in_sessionId) override;
+    ::ndk::ScopedAStatus registerEndpointHub(
+            const std::shared_ptr<IEndpointCallback>& in_callback, const HubInfo& in_hubInfo,
+            std::shared_ptr<IEndpointCommunication>* _aidl_return) override;
 
   private:
-    struct EndpointSession {
-        int32_t sessionId;
-        EndpointId initiator;
-        EndpointId peer;
-        std::optional<std::string> serviceDescriptor;
+    class HubInterface : public BnEndpointCommunication {
+      public:
+        HubInterface(ContextHub& hal, const std::shared_ptr<IEndpointCallback>& in_callback,
+                     const HubInfo& in_hubInfo)
+            : mHal(hal), mEndpointCallback(in_callback), kInfo(in_hubInfo) {}
+        ~HubInterface() = default;
+
+        ::ndk::ScopedAStatus registerEndpoint(const EndpointInfo& in_endpoint) override;
+        ::ndk::ScopedAStatus unregisterEndpoint(const EndpointInfo& in_endpoint) override;
+        ::ndk::ScopedAStatus requestSessionIdRange(int32_t in_size,
+                                                   std::array<int32_t, 2>* _aidl_return) override;
+        ::ndk::ScopedAStatus openEndpointSession(
+                int32_t in_sessionId, const EndpointId& in_destination,
+                const EndpointId& in_initiator,
+                const std::optional<std::string>& in_serviceDescriptor) override;
+        ::ndk::ScopedAStatus sendMessageToEndpoint(int32_t in_sessionId,
+                                                   const Message& in_msg) override;
+        ::ndk::ScopedAStatus sendMessageDeliveryStatusToEndpoint(
+                int32_t in_sessionId, const MessageDeliveryStatus& in_msgStatus) override;
+        ::ndk::ScopedAStatus closeEndpointSession(int32_t in_sessionId, Reason in_reason) override;
+        ::ndk::ScopedAStatus endpointSessionOpenComplete(int32_t in_sessionId) override;
+        ::ndk::ScopedAStatus unregister() override;
+
+      private:
+        friend class ContextHub;
+
+        struct EndpointSession {
+            int32_t sessionId;
+            EndpointId initiator;
+            EndpointId peer;
+            std::optional<std::string> serviceDescriptor;
+        };
+
+        //! Finds an endpoint in the range defined by the endpoints
+        //! @return whether the endpoint was found
+        template <typename Iter>
+        bool findEndpoint(const EndpointId& target, const Iter& begin, const Iter& end) {
+            for (auto iter = begin; iter != end; ++iter) {
+                if (iter->id.id == target.id && iter->id.hubId == target.hubId) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        //! Endpoint storage and information
+        ContextHub& mHal;
+        std::shared_ptr<IEndpointCallback> mEndpointCallback;
+        const HubInfo kInfo;
+
+        std::atomic<bool> mActive = true;
+
+        std::mutex mEndpointMutex;
+        std::vector<EndpointInfo> mEndpoints;
+        std::vector<EndpointSession> mEndpointSessions;
+        uint16_t mBaseSessionId;
+        uint16_t mMaxSessionId;
     };
 
     static constexpr uint32_t kMockHubId = 0;
 
-    //! Finds an endpoint in the range defined by the endpoints
-    //! @return whether the endpoint was found
-    template <typename Iter>
-    bool findEndpoint(const EndpointId& target, const Iter& begin, const Iter& end) {
-        for (auto iter = begin; iter != end; ++iter) {
-            if (iter->id.id == target.id && iter->id.hubId == target.hubId) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     std::shared_ptr<IContextHubCallback> mCallback;
 
     std::unordered_set<char16_t> mConnectedHostEndpoints;
 
-    //! Endpoint storage and information
-    std::mutex mEndpointMutex;
-    std::vector<EndpointInfo> mEndpoints;
-    std::vector<EndpointSession> mEndpointSessions;
-    std::shared_ptr<IEndpointCallback> mEndpointCallback;
-    int32_t mMaxValidSessionId = 0;
+    std::mutex mHostHubsLock;
+    std::unordered_map<int64_t, std::shared_ptr<HubInterface>> mIdToHostHub;
+    int32_t mNextSessionIdBase = 0;
 };
 
 }  // namespace contexthub
diff --git a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
index 090d4fe..d7859d9 100644
--- a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
+++ b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
@@ -24,6 +24,7 @@
 #include <android/hardware/contexthub/IContextHub.h>
 #include <android/hardware/contexthub/IContextHubCallback.h>
 #include <android/hardware/contexthub/IEndpointCallback.h>
+#include <android/hardware/contexthub/IEndpointCommunication.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 #include <log/log.h>
@@ -46,6 +47,7 @@
 using ::android::hardware::contexthub::HubInfo;
 using ::android::hardware::contexthub::IContextHub;
 using ::android::hardware::contexthub::IContextHubCallbackDefault;
+using ::android::hardware::contexthub::IEndpointCommunication;
 using ::android::hardware::contexthub::Message;
 using ::android::hardware::contexthub::MessageDeliveryStatus;
 using ::android::hardware::contexthub::NanoappBinary;
@@ -62,32 +64,71 @@
 // 6612b522-b717-41c8-b48d-c0b1cc64e142
 constexpr std::array<uint8_t, 16> kUuid = {0x66, 0x12, 0xb5, 0x22, 0xb7, 0x17, 0x41, 0xc8,
                                            0xb4, 0x8d, 0xc0, 0xb1, 0xcc, 0x64, 0xe1, 0x42};
+
 const String16 kName{"VtsAidlHalContextHubTargetTest"};
 
+const String16 kEchoServiceName{"android.hardware.contexthub.test.EchoService"};
+
+constexpr int64_t kDefaultHubId = 1;
+
+class TestEndpointCallback;
 class ContextHubAidl : public testing::TestWithParam<std::tuple<std::string, int32_t>> {
   public:
-    virtual void SetUp() override {
-        contextHub = android::waitForDeclaredService<IContextHub>(
+    void SetUp() override {
+        mContextHub = android::waitForDeclaredService<IContextHub>(
                 String16(std::get<0>(GetParam()).c_str()));
-        ASSERT_NE(contextHub, nullptr);
-
-        // Best effort enable test mode - this may not be supported on older HALS, so we
-        // ignore the return value.
-        contextHub->setTestMode(/* enable= */ true);
+        ASSERT_NE(mContextHub, nullptr);
+        mEndpointCb = sp<TestEndpointCallback>::make();
     }
 
-    virtual void TearDown() override { contextHub->setTestMode(/* enable= */ false); }
-
     uint32_t getHubId() { return std::get<1>(GetParam()); }
 
+    Status registerHub(int64_t id, sp<IEndpointCommunication>* hubInterface) {
+        HubInfo info;
+        info.hubId = id;
+        return mContextHub->registerEndpointHub(mEndpointCb, info, hubInterface);
+    }
+
+    bool registerDefaultHub() {
+        Status status = registerHub(kDefaultHubId, &mHubInterface);
+        if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
+            status.transactionError() == android::UNKNOWN_TRANSACTION) {
+            return false;
+        }
+        EXPECT_TRUE(status.isOk());
+        EXPECT_NE(mHubInterface, nullptr);
+        if (!mHubInterface) {
+            return false;
+        }
+        return true;
+    }
+
     void testSettingChanged(Setting setting);
 
-    sp<IContextHub> contextHub;
+    sp<IContextHub> mContextHub;
+    sp<TestEndpointCallback> mEndpointCb;
+    sp<IEndpointCommunication> mHubInterface;
+};
+
+class ContextHubAidlWithTestMode : public ContextHubAidl {
+  public:
+    void SetUp() override {
+        ContextHubAidl::SetUp();
+
+        // Best effort enable test mode - this may not be supported on older HALS, so we
+        // ignore the return value.
+        mContextHub->setTestMode(/* enable= */ true);
+    }
+
+    void TearDown() override {
+        mContextHub->setTestMode(/* enable= */ false);
+        ContextHubAidl::TearDown();
+    }
 };
 
 TEST_P(ContextHubAidl, TestGetHubs) {
     std::vector<ContextHubInfo> hubs;
-    ASSERT_TRUE(contextHub->getContextHubs(&hubs).isOk());
+    ASSERT_TRUE(mContextHub->getContextHubs(&hubs).isOk());
 
     ALOGD("System reports %zu hubs", hubs.size());
 
@@ -109,7 +150,7 @@
 }
 
 TEST_P(ContextHubAidl, TestEnableTestMode) {
-    Status status = contextHub->setTestMode(true);
+    Status status = mContextHub->setTestMode(true);
     if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
         status.transactionError() == android::UNKNOWN_TRANSACTION) {
         GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -119,7 +160,7 @@
 }
 
 TEST_P(ContextHubAidl, TestDisableTestMode) {
-    Status status = contextHub->setTestMode(false);
+    Status status = mContextHub->setTestMode(false);
     if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
         status.transactionError() == android::UNKNOWN_TRANSACTION) {
         GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -168,7 +209,7 @@
 
 TEST_P(ContextHubAidl, TestRegisterCallback) {
     sp<EmptyContextHubCallback> cb = sp<EmptyContextHubCallback>::make();
-    ASSERT_TRUE(contextHub->registerCallback(getHubId(), cb).isOk());
+    ASSERT_TRUE(mContextHub->registerCallback(getHubId(), cb).isOk());
 }
 
 // Helper callback that puts the async appInfo callback data into a promise
@@ -217,8 +258,8 @@
 // Calls queryApps() and checks the returned metadata
 TEST_P(ContextHubAidl, TestQueryApps) {
     sp<QueryAppsCallback> cb = sp<QueryAppsCallback>::make();
-    ASSERT_TRUE(contextHub->registerCallback(getHubId(), cb).isOk());
-    ASSERT_TRUE(contextHub->queryNanoapps(getHubId()).isOk());
+    ASSERT_TRUE(mContextHub->registerCallback(getHubId(), cb).isOk());
+    ASSERT_TRUE(mContextHub->queryNanoapps(getHubId()).isOk());
 
     std::vector<NanoappInfo> appInfoList;
     ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &appInfoList));
@@ -239,7 +280,7 @@
 // Calls getPreloadedNanoappsIds() and verifies there are preloaded nanoapps
 TEST_P(ContextHubAidl, TestGetPreloadedNanoappIds) {
     std::vector<int64_t> preloadedNanoappIds;
-    Status status = contextHub->getPreloadedNanoappIds(getHubId(), &preloadedNanoappIds);
+    Status status = mContextHub->getPreloadedNanoappIds(getHubId(), &preloadedNanoappIds);
     if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
         status.transactionError() == android::UNKNOWN_TRANSACTION) {
         GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -302,7 +343,7 @@
   public:
     virtual void SetUp() override {
         ContextHubAidl::SetUp();
-        ASSERT_TRUE(contextHub->registerCallback(getHubId(), cb).isOk());
+        ASSERT_TRUE(mContextHub->registerCallback(getHubId(), cb).isOk());
     }
 
     sp<TransactionResultCallback> cb = sp<TransactionResultCallback>::make();
@@ -316,7 +357,7 @@
     std::fill(message.messageBody.begin(), message.messageBody.end(), 0);
 
     ALOGD("Sending message to non-existent nanoapp");
-    ASSERT_TRUE(contextHub->sendMessageToHub(getHubId(), message).isOk());
+    ASSERT_TRUE(mContextHub->sendMessageToHub(getHubId(), message).isOk());
 }
 
 TEST_P(ContextHubTransactionTest, TestLoadEmptyNanoapp) {
@@ -330,7 +371,7 @@
     emptyApp.targetChreApiMinorVersion = 0;
 
     ALOGD("Loading empty nanoapp");
-    bool success = contextHub->loadNanoapp(getHubId(), emptyApp, cb->expectedTransactionId).isOk();
+    bool success = mContextHub->loadNanoapp(getHubId(), emptyApp, cb->expectedTransactionId).isOk();
     if (success) {
         bool transactionSuccess;
         ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &transactionSuccess));
@@ -343,7 +384,7 @@
 
     ALOGD("Unloading nonexistent nanoapp");
     bool success =
-            contextHub->unloadNanoapp(getHubId(), kNonExistentAppId, cb->expectedTransactionId)
+            mContextHub->unloadNanoapp(getHubId(), kNonExistentAppId, cb->expectedTransactionId)
                     .isOk();
     if (success) {
         bool transactionSuccess;
@@ -357,7 +398,7 @@
 
     ALOGD("Enabling nonexistent nanoapp");
     bool success =
-            contextHub->enableNanoapp(getHubId(), kNonExistentAppId, cb->expectedTransactionId)
+            mContextHub->enableNanoapp(getHubId(), kNonExistentAppId, cb->expectedTransactionId)
                     .isOk();
     if (success) {
         bool transactionSuccess;
@@ -371,7 +412,7 @@
 
     ALOGD("Disabling nonexistent nanoapp");
     bool success =
-            contextHub->disableNanoapp(getHubId(), kNonExistentAppId, cb->expectedTransactionId)
+            mContextHub->disableNanoapp(getHubId(), kNonExistentAppId, cb->expectedTransactionId)
                     .isOk();
     if (success) {
         bool transactionSuccess;
@@ -384,10 +425,10 @@
     // In VTS, we only test that sending the values doesn't cause things to blow up - GTS tests
     // verify the expected E2E behavior in CHRE
     sp<EmptyContextHubCallback> cb = sp<EmptyContextHubCallback>::make();
-    ASSERT_TRUE(contextHub->registerCallback(getHubId(), cb).isOk());
+    ASSERT_TRUE(mContextHub->registerCallback(getHubId(), cb).isOk());
 
-    ASSERT_TRUE(contextHub->onSettingChanged(setting, true /* enabled */).isOk());
-    ASSERT_TRUE(contextHub->onSettingChanged(setting, false /* enabled */).isOk());
+    ASSERT_TRUE(mContextHub->onSettingChanged(setting, true /* enabled */).isOk());
+    ASSERT_TRUE(mContextHub->onSettingChanged(setting, false /* enabled */).isOk());
 }
 
 TEST_P(ContextHubAidl, TestOnLocationSettingChanged) {
@@ -442,27 +483,27 @@
     hostEndpointInfo.type = HostEndpointInfo::Type::NATIVE;
     hostEndpointInfo.hostEndpointId = kHostEndpointId;
 
-    ASSERT_TRUE(contextHub->onHostEndpointConnected(hostEndpointInfo).isOk());
-    ASSERT_TRUE(contextHub->onHostEndpointDisconnected(kHostEndpointId).isOk());
+    ASSERT_TRUE(mContextHub->onHostEndpointConnected(hostEndpointInfo).isOk());
+    ASSERT_TRUE(mContextHub->onHostEndpointDisconnected(kHostEndpointId).isOk());
 }
 
 TEST_P(ContextHubTransactionTest, TestInvalidHostConnection) {
     constexpr char16_t kHostEndpointId = 1;
 
-    ASSERT_TRUE(contextHub->onHostEndpointDisconnected(kHostEndpointId).isOk());
+    ASSERT_TRUE(mContextHub->onHostEndpointDisconnected(kHostEndpointId).isOk());
 }
 
 TEST_P(ContextHubTransactionTest, TestNanSessionStateChange) {
     NanSessionStateUpdate update;
     update.state = true;
-    Status status = contextHub->onNanSessionStateChanged(update);
+    Status status = mContextHub->onNanSessionStateChanged(update);
     if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
         status.transactionError() == android::UNKNOWN_TRANSACTION) {
         GTEST_SKIP() << "Not supported -> old API; or not implemented";
     } else {
         ASSERT_TRUE(status.isOk());
         update.state = false;
-        ASSERT_TRUE(contextHub->onNanSessionStateChanged(update).isOk());
+        ASSERT_TRUE(mContextHub->onNanSessionStateChanged(update).isOk());
     }
 }
 
@@ -471,7 +512,7 @@
     messageDeliveryStatus.messageSequenceNumber = 123;
     messageDeliveryStatus.errorCode = ErrorCode::OK;
 
-    Status status = contextHub->sendMessageDeliveryStatusToHub(getHubId(), messageDeliveryStatus);
+    Status status = mContextHub->sendMessageDeliveryStatusToHub(getHubId(), messageDeliveryStatus);
     if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
         status.transactionError() == android::UNKNOWN_TRANSACTION) {
         GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -492,7 +533,11 @@
     }
 
     Status onMessageReceived(int32_t /* sessionId */, const Message& message) override {
-        mMessages.push_back(message);
+        {
+            std::unique_lock<std::mutex> lock(mMutex);
+            mMessages.push_back(message);
+        }
+        mCondVar.notify_one();
         return Status::ok();
     }
 
@@ -513,25 +558,57 @@
     }
 
     Status onEndpointSessionOpenComplete(int32_t /* sessionId */) override {
-        mWasOnEndpointSessionOpenCompleteCalled = true;
+        {
+            std::unique_lock<std::mutex> lock(mMutex);
+            mWasOnEndpointSessionOpenCompleteCalled = true;
+        }
+        mCondVar.notify_one();
         return Status::ok();
     }
 
-    std::vector<Message> getMessages() { return mMessages; }
-
     bool wasOnEndpointSessionOpenCompleteCalled() {
         return mWasOnEndpointSessionOpenCompleteCalled;
     }
+
     void resetWasOnEndpointSessionOpenCompleteCalled() {
         mWasOnEndpointSessionOpenCompleteCalled = false;
     }
 
+    std::mutex& getMutex() { return mMutex; }
+    std::condition_variable& getCondVar() { return mCondVar; }
+    std::vector<Message> getMessages() { return mMessages; }
+
   private:
     std::vector<Message> mMessages;
+    std::mutex mMutex;
+    std::condition_variable mCondVar;
     bool mWasOnEndpointSessionOpenCompleteCalled = false;
 };
 
-TEST_P(ContextHubAidl, RegisterEndpoint) {
+TEST_P(ContextHubAidlWithTestMode, RegisterHub) {
+    if (!registerDefaultHub()) {
+        GTEST_SKIP() << "Not supported -> old API; or not implemented";
+    }
+
+    sp<IEndpointCommunication> hub2;
+    Status status = registerHub(kDefaultHubId + 1, &hub2);
+    EXPECT_TRUE(status.isOk());
+
+    sp<IEndpointCommunication> hub3;
+    status = registerHub(kDefaultHubId + 1, &hub3);
+    ASSERT_FALSE(status.isOk());
+    EXPECT_EQ(status.exceptionCode(), Status::EX_ILLEGAL_STATE);
+
+    hub2->unregister();
+    status = registerHub(kDefaultHubId + 1, &hub3);
+    EXPECT_TRUE(status.isOk());
+}
+
+TEST_P(ContextHubAidlWithTestMode, RegisterEndpoint) {
+    if (!registerDefaultHub()) {
+        GTEST_SKIP() << "Not supported -> old API; or not implemented";
+    }
+
     EndpointInfo endpointInfo;
     endpointInfo.id.id = 1;
     endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
@@ -539,7 +616,7 @@
     endpointInfo.name = String16("Test host endpoint 1");
     endpointInfo.version = 42;
 
-    Status status = contextHub->registerEndpoint(endpointInfo);
+    Status status = mHubInterface->registerEndpoint(endpointInfo);
     if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
         status.transactionError() == android::UNKNOWN_TRANSACTION) {
         GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -548,7 +625,11 @@
     }
 }
 
-TEST_P(ContextHubAidl, RegisterEndpointSameNameFailure) {
+TEST_P(ContextHubAidlWithTestMode, RegisterEndpointSameNameFailure) {
+    if (!registerDefaultHub()) {
+        GTEST_SKIP() << "Not supported -> old API; or not implemented";
+    }
+
     EndpointInfo endpointInfo;
     endpointInfo.id.id = 2;
     endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
@@ -563,7 +644,7 @@
     endpointInfo2.name = String16("Test host endpoint 2");
     endpointInfo2.version = 42;
 
-    Status status = contextHub->registerEndpoint(endpointInfo);
+    Status status = mHubInterface->registerEndpoint(endpointInfo);
     if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
         status.transactionError() == android::UNKNOWN_TRANSACTION) {
         GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -571,10 +652,14 @@
         EXPECT_TRUE(status.isOk());
     }
 
-    EXPECT_FALSE(contextHub->registerEndpoint(endpointInfo2).isOk());
+    EXPECT_FALSE(mHubInterface->registerEndpoint(endpointInfo2).isOk());
 }
 
-TEST_P(ContextHubAidl, RegisterEndpointSameIdFailure) {
+TEST_P(ContextHubAidlWithTestMode, RegisterEndpointSameIdFailure) {
+    if (!registerDefaultHub()) {
+        GTEST_SKIP() << "Not supported -> old API; or not implemented";
+    }
+
     EndpointInfo endpointInfo;
     endpointInfo.id.id = 4;
     endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
@@ -589,7 +674,7 @@
     endpointInfo2.name = String16("Test host endpoint - same ID test");
     endpointInfo2.version = 42;
 
-    Status status = contextHub->registerEndpoint(endpointInfo);
+    Status status = mHubInterface->registerEndpoint(endpointInfo);
     if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
         status.transactionError() == android::UNKNOWN_TRANSACTION) {
         GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -597,10 +682,14 @@
         EXPECT_TRUE(status.isOk());
     }
 
-    EXPECT_FALSE(contextHub->registerEndpoint(endpointInfo2).isOk());
+    EXPECT_FALSE(mHubInterface->registerEndpoint(endpointInfo2).isOk());
 }
 
-TEST_P(ContextHubAidl, UnregisterEndpoint) {
+TEST_P(ContextHubAidlWithTestMode, UnregisterEndpoint) {
+    if (!registerDefaultHub()) {
+        GTEST_SKIP() << "Not supported -> old API; or not implemented";
+    }
+
     EndpointInfo endpointInfo;
     endpointInfo.id.id = 6;
     endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
@@ -608,7 +697,7 @@
     endpointInfo.name = String16("Test host endpoint 6");
     endpointInfo.version = 42;
 
-    Status status = contextHub->registerEndpoint(endpointInfo);
+    Status status = mHubInterface->registerEndpoint(endpointInfo);
     if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
         status.transactionError() == android::UNKNOWN_TRANSACTION) {
         GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -616,10 +705,14 @@
         EXPECT_TRUE(status.isOk());
     }
 
-    EXPECT_TRUE(contextHub->unregisterEndpoint(endpointInfo).isOk());
+    EXPECT_TRUE(mHubInterface->unregisterEndpoint(endpointInfo).isOk());
 }
 
-TEST_P(ContextHubAidl, UnregisterEndpointNonexistent) {
+TEST_P(ContextHubAidlWithTestMode, UnregisterEndpointNonexistent) {
+    if (!registerDefaultHub()) {
+        GTEST_SKIP() << "Not supported -> old API; or not implemented";
+    }
+
     EndpointInfo endpointInfo;
     endpointInfo.id.id = 100;
     endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
@@ -627,7 +720,7 @@
     endpointInfo.name = String16("Test host endpoint 100");
     endpointInfo.version = 42;
 
-    Status status = contextHub->unregisterEndpoint(endpointInfo);
+    Status status = mHubInterface->unregisterEndpoint(endpointInfo);
     if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
         status.transactionError() == android::UNKNOWN_TRANSACTION) {
         GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -636,25 +729,9 @@
     }
 }
 
-TEST_P(ContextHubAidl, RegisterCallback) {
-    auto cb = sp<TestEndpointCallback>::make();
-    Status status = contextHub->registerEndpointCallback(cb);
-    if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
-        status.transactionError() == android::UNKNOWN_TRANSACTION) {
+TEST_P(ContextHubAidlWithTestMode, OpenEndpointSessionInvalidRange) {
+    if (!registerDefaultHub()) {
         GTEST_SKIP() << "Not supported -> old API; or not implemented";
-    } else {
-        EXPECT_TRUE(status.isOk());
-    }
-}
-
-TEST_P(ContextHubAidl, OpenEndpointSessionInvalidRange) {
-    auto cb = sp<TestEndpointCallback>::make();
-    Status status = contextHub->registerEndpointCallback(cb);
-    if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
-        status.transactionError() == android::UNKNOWN_TRANSACTION) {
-        GTEST_SKIP() << "Not supported -> old API; or not implemented";
-    } else {
-        EXPECT_TRUE(status.isOk());
     }
 
     // Register the endpoint
@@ -664,15 +741,15 @@
     initiatorEndpoint.type = EndpointInfo::EndpointType::NATIVE;
     initiatorEndpoint.name = String16("Test host endpoint 7");
     initiatorEndpoint.version = 42;
-    EXPECT_TRUE(contextHub->registerEndpoint(initiatorEndpoint).isOk());
+    EXPECT_TRUE(mHubInterface->registerEndpoint(initiatorEndpoint).isOk());
 
     // Find the destination, if it exists
     std::vector<EndpointInfo> endpoints;
-    EXPECT_TRUE(contextHub->getEndpoints(&endpoints).isOk());
+    EXPECT_TRUE(mContextHub->getEndpoints(&endpoints).isOk());
     const EndpointInfo* destinationEndpoint = nullptr;
     for (const EndpointInfo& endpoint : endpoints) {
         for (const Service& service : endpoint.services) {
-            if (service.serviceDescriptor == String16("ECHO")) {
+            if (service.serviceDescriptor == kEchoServiceName) {
                 destinationEndpoint = &endpoint;
                 break;
             }
@@ -685,30 +762,24 @@
     // Request the range
     constexpr int32_t requestedRange = 100;
     std::array<int32_t, 2> range;
-    ASSERT_TRUE(contextHub->requestSessionIdRange(requestedRange, &range).isOk());
+    ASSERT_TRUE(mHubInterface->requestSessionIdRange(requestedRange, &range).isOk());
     EXPECT_EQ(range.size(), 2);
     EXPECT_GE(range[1] - range[0] + 1, requestedRange);
 
     // Open the session
-    cb->resetWasOnEndpointSessionOpenCompleteCalled();
     int32_t sessionId = range[1] + 10;  // invalid
-    EXPECT_FALSE(contextHub
+    EXPECT_FALSE(mHubInterface
                          ->openEndpointSession(sessionId, destinationEndpoint->id,
                                                initiatorEndpoint.id,
-                                               /* in_serviceDescriptor= */ String16("ECHO"))
+                                               /* in_serviceDescriptor= */ kEchoServiceName)
                          .isOk());
-    EXPECT_FALSE(cb->wasOnEndpointSessionOpenCompleteCalled());
 }
 
-TEST_P(ContextHubAidl, OpenEndpointSessionAndSendMessageEchoesBack) {
-    auto cb = sp<TestEndpointCallback>::make();
-    Status status = contextHub->registerEndpointCallback(cb);
-    if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
-        status.transactionError() == android::UNKNOWN_TRANSACTION) {
+TEST_P(ContextHubAidlWithTestMode, OpenEndpointSessionAndSendMessageEchoesBack) {
+    if (!registerDefaultHub()) {
         GTEST_SKIP() << "Not supported -> old API; or not implemented";
-    } else {
-        EXPECT_TRUE(status.isOk());
     }
+    std::unique_lock<std::mutex> lock(mEndpointCb->getMutex());
 
     // Register the endpoint
     EndpointInfo initiatorEndpoint;
@@ -717,15 +788,15 @@
     initiatorEndpoint.type = EndpointInfo::EndpointType::NATIVE;
     initiatorEndpoint.name = String16("Test host endpoint 7");
     initiatorEndpoint.version = 42;
-    EXPECT_TRUE(contextHub->registerEndpoint(initiatorEndpoint).isOk());
+    EXPECT_TRUE(mHubInterface->registerEndpoint(initiatorEndpoint).isOk());
 
     // Find the destination, if it exists
     std::vector<EndpointInfo> endpoints;
-    EXPECT_TRUE(contextHub->getEndpoints(&endpoints).isOk());
+    EXPECT_TRUE(mContextHub->getEndpoints(&endpoints).isOk());
     const EndpointInfo* destinationEndpoint = nullptr;
     for (const EndpointInfo& endpoint : endpoints) {
         for (const Service& service : endpoint.services) {
-            if (service.serviceDescriptor == String16("ECHO")) {
+            if (service.serviceDescriptor == kEchoServiceName) {
                 destinationEndpoint = &endpoint;
                 break;
             }
@@ -738,30 +809,32 @@
     // Request the range
     constexpr int32_t requestedRange = 100;
     std::array<int32_t, 2> range;
-    ASSERT_TRUE(contextHub->requestSessionIdRange(requestedRange, &range).isOk());
+    ASSERT_TRUE(mHubInterface->requestSessionIdRange(requestedRange, &range).isOk());
     EXPECT_EQ(range.size(), 2);
     EXPECT_GE(range[1] - range[0] + 1, requestedRange);
 
     // Open the session
-    cb->resetWasOnEndpointSessionOpenCompleteCalled();
+    mEndpointCb->resetWasOnEndpointSessionOpenCompleteCalled();
     int32_t sessionId = range[0];
-    ASSERT_TRUE(contextHub
+    ASSERT_TRUE(mHubInterface
                         ->openEndpointSession(sessionId, destinationEndpoint->id,
                                               initiatorEndpoint.id,
-                                              /* in_serviceDescriptor= */ String16("ECHO"))
+                                              /* in_serviceDescriptor= */ kEchoServiceName)
                         .isOk());
-    EXPECT_TRUE(cb->wasOnEndpointSessionOpenCompleteCalled());
+    mEndpointCb->getCondVar().wait(lock);
+    EXPECT_TRUE(mEndpointCb->wasOnEndpointSessionOpenCompleteCalled());
 
     // Send the message
     Message message;
     message.flags = 0;
     message.sequenceNumber = 0;
     message.content.push_back(42);
-    ASSERT_TRUE(contextHub->sendMessageToEndpoint(sessionId, message).isOk());
+    ASSERT_TRUE(mHubInterface->sendMessageToEndpoint(sessionId, message).isOk());
 
     // Check for echo
-    EXPECT_FALSE(cb->getMessages().empty());
-    EXPECT_EQ(cb->getMessages().back().content.back(), 42);
+    mEndpointCb->getCondVar().wait(lock);
+    EXPECT_FALSE(mEndpointCb->getMessages().empty());
+    EXPECT_EQ(mEndpointCb->getMessages().back().content.back(), 42);
 }
 
 std::string PrintGeneratedTest(const testing::TestParamInfo<ContextHubAidl::ParamType>& info) {
@@ -772,13 +845,17 @@
 INSTANTIATE_TEST_SUITE_P(ContextHub, ContextHubAidl, testing::ValuesIn(generateContextHubMapping()),
                          PrintGeneratedTest);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ContextHubAidlWithTestMode);
+INSTANTIATE_TEST_SUITE_P(ContextHub, ContextHubAidlWithTestMode,
+                         testing::ValuesIn(generateContextHubMapping()), PrintGeneratedTest);
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ContextHubTransactionTest);
 INSTANTIATE_TEST_SUITE_P(ContextHub, ContextHubTransactionTest,
                          testing::ValuesIn(generateContextHubMapping()), PrintGeneratedTest);
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
-    ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ProcessState::self()->setThreadPoolMaxThreadCount(2);
     ProcessState::self()->startThreadPool();
     return RUN_ALL_TESTS();
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/AuxiliaryInformation.aidl
similarity index 78%
copy from wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl
copy to gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/AuxiliaryInformation.aidl
index 0600def..a0351f8 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/AuxiliaryInformation.aidl
@@ -31,11 +31,19 @@
 // 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.wifi.supplicant;
-@Backing(type="int") @VintfStability
-enum UsdReasonCode {
-  FAILURE_UNKNOWN = 0,
-  TIMEOUT = 1,
-  USER_REQUESTED = 2,
-  INVALID_ARGS = 3,
+package android.hardware.gnss.gnss_assistance;
+/* @hide */
+@VintfStability
+parcelable AuxiliaryInformation {
+  int svid;
+  android.hardware.gnss.GnssSignalType[] availableSignalTypes;
+  int frequencyChannelNumber;
+  android.hardware.gnss.gnss_assistance.AuxiliaryInformation.BeidouB1CSatelliteOrbitType satType;
+  @Backing(type="int") @VintfStability
+  enum BeidouB1CSatelliteOrbitType {
+    UNDEFINED = 0,
+    GEO = 1,
+    IGSO = 2,
+    MEO = 3,
+  }
 }
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/BeidouSatelliteEphemeris.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/BeidouSatelliteEphemeris.aidl
index ec517e6..f7b16f9 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/BeidouSatelliteEphemeris.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/BeidouSatelliteEphemeris.aidl
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -20,7 +35,7 @@
 /* @hide */
 @VintfStability
 parcelable BeidouSatelliteEphemeris {
-  int prn;
+  int svid;
   android.hardware.gnss.gnss_assistance.BeidouSatelliteEphemeris.BeidouSatelliteClockModel satelliteClockModel;
   android.hardware.gnss.gnss_assistance.KeplerianOrbitModel satelliteOrbitModel;
   android.hardware.gnss.gnss_assistance.BeidouSatelliteEphemeris.BeidouSatelliteHealth satelliteHealth;
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GalileoSatelliteEphemeris.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GalileoSatelliteEphemeris.aidl
index 1bac08e..e5a3630 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GalileoSatelliteEphemeris.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GalileoSatelliteEphemeris.aidl
@@ -35,7 +35,7 @@
 /* @hide */
 @VintfStability
 parcelable GalileoSatelliteEphemeris {
-  int satelliteCodeNumber;
+  int svid;
   android.hardware.gnss.gnss_assistance.GalileoSatelliteEphemeris.GalileoSatelliteClockModel[] satelliteClockModel;
   android.hardware.gnss.gnss_assistance.KeplerianOrbitModel satelliteOrbitModel;
   android.hardware.gnss.gnss_assistance.GalileoSatelliteEphemeris.GalileoSvHealth svHealth;
@@ -58,11 +58,23 @@
   }
   @VintfStability
   parcelable GalileoSvHealth {
-    int dataValidityStatusE1b;
-    int signalHealthStatusE1b;
-    int dataValidityStatusE5a;
-    int signalHealthStatusE5a;
-    int dataValidityStatusE5b;
-    int signalHealthStatusE5b;
+    android.hardware.gnss.gnss_assistance.GalileoSatelliteEphemeris.GalileoSvHealth.GalileoHealthDataVaidityType dataValidityStatusE1b;
+    android.hardware.gnss.gnss_assistance.GalileoSatelliteEphemeris.GalileoSvHealth.GalileoHealthStatusType signalHealthStatusE1b;
+    android.hardware.gnss.gnss_assistance.GalileoSatelliteEphemeris.GalileoSvHealth.GalileoHealthDataVaidityType dataValidityStatusE5a;
+    android.hardware.gnss.gnss_assistance.GalileoSatelliteEphemeris.GalileoSvHealth.GalileoHealthStatusType signalHealthStatusE5a;
+    android.hardware.gnss.gnss_assistance.GalileoSatelliteEphemeris.GalileoSvHealth.GalileoHealthDataVaidityType dataValidityStatusE5b;
+    android.hardware.gnss.gnss_assistance.GalileoSatelliteEphemeris.GalileoSvHealth.GalileoHealthStatusType signalHealthStatusE5b;
+    @Backing(type="int") @VintfStability
+    enum GalileoHealthDataVaidityType {
+      DATA_VALID = 0,
+      WORKING_WITHOUT_GUARANTEE = 1,
+    }
+    @Backing(type="int") @VintfStability
+    enum GalileoHealthStatusType {
+      OK = 0,
+      OUT_OF_SERVICE = 1,
+      EXTENDED_OPERATION_MODE = 2,
+      IN_TEST = 3,
+    }
   }
 }
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GlonassAlmanac.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GlonassAlmanac.aidl
index 55621ab..1d2b30a 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GlonassAlmanac.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GlonassAlmanac.aidl
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -21,12 +36,14 @@
 @VintfStability
 parcelable GlonassAlmanac {
   long issueDateMs;
-  android.hardware.gnss.gnss_assistance.GlonassAlmanac.GlonassSatelliteAlmanac[] satelliteAlmanac;
+  android.hardware.gnss.gnss_assistance.GlonassAlmanac.GlonassSatelliteAlmanac[] satelliteAlmanacs;
   @VintfStability
   parcelable GlonassSatelliteAlmanac {
     int slotNumber;
     int svHealth;
-    int frequencyChannel;
+    int frequencyChannelNumber;
+    int calendarDayNumber;
+    boolean isGlonassM;
     double tau;
     double tLambda;
     double lambda;
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GlonassSatelliteEphemeris.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GlonassSatelliteEphemeris.aidl
index bbcb3af..a08268b 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GlonassSatelliteEphemeris.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GlonassSatelliteEphemeris.aidl
@@ -39,14 +39,20 @@
   int svHealth;
   double frameTimeSeconds;
   int ageInDays;
+  int updateIntervalMinutes;
+  boolean isOddUpdateInterval;
+  boolean isGlonassM;
   android.hardware.gnss.gnss_assistance.GlonassSatelliteEphemeris.GlonassSatelliteClockModel satelliteClockModel;
   android.hardware.gnss.gnss_assistance.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel satelliteOrbitModel;
+  const int GLONASS_HEALTH_STATUS_HEALTHY = 0;
+  const int GLONASS_HEALTH_STATUS_UNHEALTHY = 1;
   @VintfStability
   parcelable GlonassSatelliteClockModel {
     long timeOfClockSeconds;
     double clockBias;
-    double freqBias;
-    int freqNumber;
+    double frequencyBias;
+    int frequencyChannelNumber;
+    double groupDelayDiffSeconds;
   }
   @VintfStability
   parcelable GlonassSatelliteOrbitModel {
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GnssAlmanac.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GnssAlmanac.aidl
index b986be4..a7f3f5c 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GnssAlmanac.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GnssAlmanac.aidl
@@ -36,10 +36,11 @@
 @VintfStability
 parcelable GnssAlmanac {
   long issueDateMs;
-  int iod;
+  int ioda;
   int weekNumber;
   int toaSeconds;
-  android.hardware.gnss.gnss_assistance.GnssAlmanac.GnssSatelliteAlmanac[] satelliteAlmanac;
+  boolean isCompleteAlmanacProvided;
+  android.hardware.gnss.gnss_assistance.GnssAlmanac.GnssSatelliteAlmanac[] satelliteAlmanacs;
   @VintfStability
   parcelable GnssSatelliteAlmanac {
     int svid;
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GnssAssistance.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GnssAssistance.aidl
index 1df2123..5f7b886 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GnssAssistance.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GnssAssistance.aidl
@@ -55,6 +55,7 @@
     android.hardware.gnss.gnss_assistance.GpsSatelliteEphemeris[] satelliteEphemeris;
     android.hardware.gnss.gnss_assistance.RealTimeIntegrityModel[] realTimeIntegrityModels;
     android.hardware.gnss.gnss_assistance.GnssAssistance.GnssSatelliteCorrections[] satelliteCorrections;
+    android.hardware.gnss.gnss_assistance.AuxiliaryInformation auxiliaryInformation;
   }
   @VintfStability
   parcelable GalileoAssistance {
@@ -66,6 +67,7 @@
     android.hardware.gnss.gnss_assistance.GalileoSatelliteEphemeris[] satelliteEphemeris;
     android.hardware.gnss.gnss_assistance.RealTimeIntegrityModel[] realTimeIntegrityModels;
     android.hardware.gnss.gnss_assistance.GnssAssistance.GnssSatelliteCorrections[] satelliteCorrections;
+    android.hardware.gnss.gnss_assistance.AuxiliaryInformation auxiliaryInformation;
   }
   @VintfStability
   parcelable GlonassAssistance {
@@ -74,6 +76,7 @@
     android.hardware.gnss.gnss_assistance.TimeModel[] timeModels;
     android.hardware.gnss.gnss_assistance.GlonassSatelliteEphemeris[] satelliteEphemeris;
     android.hardware.gnss.gnss_assistance.GnssAssistance.GnssSatelliteCorrections[] satelliteCorrections;
+    android.hardware.gnss.gnss_assistance.AuxiliaryInformation auxiliaryInformation;
   }
   @VintfStability
   parcelable QzssAssistance {
@@ -85,6 +88,7 @@
     android.hardware.gnss.gnss_assistance.QzssSatelliteEphemeris[] satelliteEphemeris;
     android.hardware.gnss.gnss_assistance.RealTimeIntegrityModel[] realTimeIntegrityModels;
     android.hardware.gnss.gnss_assistance.GnssAssistance.GnssSatelliteCorrections[] satelliteCorrections;
+    android.hardware.gnss.gnss_assistance.AuxiliaryInformation auxiliaryInformation;
   }
   @VintfStability
   parcelable BeidouAssistance {
@@ -96,5 +100,6 @@
     android.hardware.gnss.gnss_assistance.BeidouSatelliteEphemeris[] satelliteEphemeris;
     android.hardware.gnss.gnss_assistance.RealTimeIntegrityModel[] realTimeIntegrityModels;
     android.hardware.gnss.gnss_assistance.GnssAssistance.GnssSatelliteCorrections[] satelliteCorrections;
+    android.hardware.gnss.gnss_assistance.AuxiliaryInformation auxiliaryInformation;
   }
 }
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GpsSatelliteEphemeris.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GpsSatelliteEphemeris.aidl
index 721edb4..8a2ab77 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GpsSatelliteEphemeris.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/GpsSatelliteEphemeris.aidl
@@ -35,7 +35,7 @@
 /* @hide */
 @VintfStability
 parcelable GpsSatelliteEphemeris {
-  int prn;
+  int svid;
   android.hardware.gnss.gnss_assistance.GpsSatelliteEphemeris.GpsL2Params gpsL2Params;
   android.hardware.gnss.gnss_assistance.GpsSatelliteEphemeris.GpsSatelliteClockModel satelliteClockModel;
   android.hardware.gnss.gnss_assistance.KeplerianOrbitModel satelliteOrbitModel;
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/QzssSatelliteEphemeris.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/QzssSatelliteEphemeris.aidl
index 5bb1c97..5f13167 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/QzssSatelliteEphemeris.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/QzssSatelliteEphemeris.aidl
@@ -35,7 +35,7 @@
 /* @hide */
 @VintfStability
 parcelable QzssSatelliteEphemeris {
-  int prn;
+  int svid;
   android.hardware.gnss.gnss_assistance.GpsSatelliteEphemeris.GpsL2Params gpsL2Params;
   android.hardware.gnss.gnss_assistance.GpsSatelliteEphemeris.GpsSatelliteClockModel satelliteClockModel;
   android.hardware.gnss.gnss_assistance.KeplerianOrbitModel satelliteOrbitModel;
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/RealTimeIntegrityModel.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/RealTimeIntegrityModel.aidl
index c7379e1..21ba2ef 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/RealTimeIntegrityModel.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/gnss_assistance/RealTimeIntegrityModel.aidl
@@ -35,8 +35,8 @@
 /* @hide */
 @VintfStability
 parcelable RealTimeIntegrityModel {
-  int svid;
-  boolean usable;
+  int badSvid;
+  android.hardware.gnss.GnssSignalType[] badSignalTypes;
   long publishDateSeconds;
   long startDateSeconds;
   long endDateSeconds;
diff --git a/gnss/aidl/android/hardware/gnss/gnss_assistance/AuxiliaryInformation.aidl b/gnss/aidl/android/hardware/gnss/gnss_assistance/AuxiliaryInformation.aidl
new file mode 100644
index 0000000..f6c6cb9
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/gnss_assistance/AuxiliaryInformation.aidl
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss.gnss_assistance;
+
+import android.hardware.gnss.GnssSignalType;
+
+/**
+ * Contains parameters to provide additional information dependent on the GNSS constellation.
+ *
+ * @hide
+ */
+@VintfStability
+parcelable AuxiliaryInformation {
+    /**
+     * BDS B1C Satellite orbit type.
+     *
+     * This is defined in BDS-SIS-ICD-B1I-3.0, section 3.1.
+     */
+    @VintfStability
+    @Backing(type="int")
+    enum BeidouB1CSatelliteOrbitType {
+        UNDEFINED = 0,
+        GEO = 1,
+        IGSO = 2,
+        MEO = 3
+    }
+
+    /**
+     * Pseudo-random or satellite ID number for the satellite, a.k.a. Space Vehicle (SV), or
+     * OSN number for Glonass. The distinction is made by looking at the constellation field.
+     * Values must be in the range of:
+     *
+     * - GPS:    1-32
+     * - Glonass: 1-25
+     * - QZSS:    183-206
+     * - Galileo: 1-36
+     * - Beidou:  1-63
+     */
+    int svid;
+
+    /** The list of available signal types for the satellite. */
+    GnssSignalType[] availableSignalTypes;
+
+    /**
+     * Glonass carrier frequency number of the satellite. This is required for Glonass.
+     *
+     * This is defined in Glonass ICD v5.1 section 3.3.1.1.
+     */
+    int frequencyChannelNumber;
+
+    /** BDS B1C satellite orbit type. This is required for Beidou. */
+    BeidouB1CSatelliteOrbitType satType;
+}
diff --git a/gnss/aidl/android/hardware/gnss/gnss_assistance/BeidouSatelliteEphemeris.aidl b/gnss/aidl/android/hardware/gnss/gnss_assistance/BeidouSatelliteEphemeris.aidl
index bb1f7d9..4c5a05b 100644
--- a/gnss/aidl/android/hardware/gnss/gnss_assistance/BeidouSatelliteEphemeris.aidl
+++ b/gnss/aidl/android/hardware/gnss/gnss_assistance/BeidouSatelliteEphemeris.aidl
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package android.hardware.gnss.gnss_assistance;
 
 import android.hardware.gnss.gnss_assistance.KeplerianOrbitModel;
@@ -81,8 +97,8 @@
         int toeSeconds;
     }
 
-    /** The PRN number of the Beidou satellite. */
-    int prn;
+    /** PRN or satellite ID number for the Beidou satellite. */
+    int svid;
 
     /** Satellite clock model. */
     BeidouSatelliteClockModel satelliteClockModel;
diff --git a/gnss/aidl/android/hardware/gnss/gnss_assistance/GalileoSatelliteEphemeris.aidl b/gnss/aidl/android/hardware/gnss/gnss_assistance/GalileoSatelliteEphemeris.aidl
index e3160ef..1af07e5 100644
--- a/gnss/aidl/android/hardware/gnss/gnss_assistance/GalileoSatelliteEphemeris.aidl
+++ b/gnss/aidl/android/hardware/gnss/gnss_assistance/GalileoSatelliteEphemeris.aidl
@@ -82,27 +82,45 @@
      */
     @VintfStability
     parcelable GalileoSvHealth {
+        /** Galileo health data validity type. */
+        @VintfStability
+        @Backing(type="int")
+        enum GalileoHealthDataVaidityType {
+            DATA_VALID = 0,
+            WORKING_WITHOUT_GUARANTEE = 1
+        }
+
+        /** Galileo health status type. */
+        @VintfStability
+        @Backing(type="int")
+        enum GalileoHealthStatusType {
+            OK = 0,
+            OUT_OF_SERVICE = 1,
+            EXTENDED_OPERATION_MODE = 2,
+            IN_TEST = 3
+        }
+
         /** E1-B data validity status. */
-        int dataValidityStatusE1b;
+        GalileoHealthDataVaidityType dataValidityStatusE1b;
 
         /** E1-B/C signal health status. */
-        int signalHealthStatusE1b;
+        GalileoHealthStatusType signalHealthStatusE1b;
 
         /** E5a data validity status. */
-        int dataValidityStatusE5a;
+        GalileoHealthDataVaidityType dataValidityStatusE5a;
 
         /** E5a signal health status. */
-        int signalHealthStatusE5a;
+        GalileoHealthStatusType signalHealthStatusE5a;
 
         /** E5b data validity status. */
-        int dataValidityStatusE5b;
+        GalileoHealthDataVaidityType dataValidityStatusE5b;
 
         /** E5b signal health status. */
-        int signalHealthStatusE5b;
+        GalileoHealthStatusType signalHealthStatusE5b;
     }
 
-    /** Satellite code number. */
-    int satelliteCodeNumber;
+    /** PRN or satellite ID number for the Galileo satellite. */
+    int svid;
 
     /** Array of satellite clock model. */
     GalileoSatelliteClockModel[] satelliteClockModel;
diff --git a/gnss/aidl/android/hardware/gnss/gnss_assistance/GlonassAlmanac.aidl b/gnss/aidl/android/hardware/gnss/gnss_assistance/GlonassAlmanac.aidl
index d4f149d..ebf6c05 100644
--- a/gnss/aidl/android/hardware/gnss/gnss_assistance/GlonassAlmanac.aidl
+++ b/gnss/aidl/android/hardware/gnss/gnss_assistance/GlonassAlmanac.aidl
@@ -1,8 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package android.hardware.gnss.gnss_assistance;
 
 /**
  * Contains Glonass almanac data.
- * This is defined in Glonass ICD v5.1, Section 4.5.
+ * This is defined in Glonass ICD v5.1, section 4.5.
  *
  * @hide
  */
@@ -10,18 +26,31 @@
 parcelable GlonassAlmanac {
     /**
      * Contains Glonass satellite almanac data.
-     * This is defined in Glonass ICD v5.1, Section 4.5.
+     * This is defined in Glonass ICD v5.1, section 4.5.
      */
     @VintfStability
     parcelable GlonassSatelliteAlmanac {
         /** Slot number. */
         int slotNumber;
 
-        /** Satellite health (0=healthy, 1=unhealthy). */
+        /**
+         * Satellite health which is set with the GLONASS_HEALTH_STATUS_*
+         * constants in GlonassSatelliteEphemeris.
+         */
         int svHealth;
 
-        /** Frequency channel number. */
-        int frequencyChannel;
+        /**
+         * Frequency channel number.
+         *
+         * This is defined in Glonass ICD v5.1 section 3.3.1.1.
+         */
+        int frequencyChannelNumber;
+
+        /** Calendar day number within the four-year period beginning since the leap year. */
+        int calendarDayNumber;
+
+        /** Flag to indicates if the satellite is a GLONASS-M satellitee. */
+        boolean isGlonassM;
 
         /** Coarse value of satellite time correction to GLONASS time in seconds. */
         double tau;
@@ -44,7 +73,7 @@
         /** Eccentricity. */
         double eccentricity;
 
-        /** Argument of perigee in radians. */
+        /** Argument of perigee in semi-circles. */
         double omega;
     }
 
@@ -52,5 +81,5 @@
     long issueDateMs;
 
     /** Array of GlonassSatelliteAlmanac. */
-    GlonassSatelliteAlmanac[] satelliteAlmanac;
+    GlonassSatelliteAlmanac[] satelliteAlmanacs;
 }
diff --git a/gnss/aidl/android/hardware/gnss/gnss_assistance/GlonassSatelliteEphemeris.aidl b/gnss/aidl/android/hardware/gnss/gnss_assistance/GlonassSatelliteEphemeris.aidl
index bebde51..e17de59 100644
--- a/gnss/aidl/android/hardware/gnss/gnss_assistance/GlonassSatelliteEphemeris.aidl
+++ b/gnss/aidl/android/hardware/gnss/gnss_assistance/GlonassSatelliteEphemeris.aidl
@@ -16,8 +16,6 @@
 
 package android.hardware.gnss.gnss_assistance;
 
-import android.hardware.gnss.gnss_assistance.SatelliteEphemerisTime;
-
 /**
  * Contains ephemeris parameters specific to Glonass satellites.
  * This is defined in RINEX 3.05 APPENDIX 10 and Glonass ICD v5.1, section 4.4.
@@ -41,10 +39,21 @@
         double clockBias;
 
         /** Frequency bias (+GammaN). */
-        double freqBias;
+        double frequencyBias;
 
-        /** Frequency number. */
-        int freqNumber;
+        /**
+         * Frequency channel number.
+         *
+         * This is defined in Glonass ICD v5.1 section 3.3.1.1.
+         */
+        int frequencyChannelNumber;
+
+        /**
+         * L1/L2 group delay difference in seconds (DeltaTau).
+         *
+         * It is set to 0.999999999999E+09 if the value is not available.
+         */
+        double groupDelayDiffSeconds;
     }
 
     /** Contains Glonass orbit model parameters in PZ-90 coordinate system. */
@@ -78,13 +87,16 @@
         double zAccel;
     }
 
-    /**
-     * L1/Satellite system (R), satellite number (slot number in sat.
-     * constellation).
-     */
+    /** Glonass health status healthy. */
+    const int GLONASS_HEALTH_STATUS_HEALTHY = 0;
+
+    /** Glonass health status unhealthy. */
+    const int GLONASS_HEALTH_STATUS_UNHEALTHY = 1;
+
+    /** Slot number. */
     int slotNumber;
 
-    /** Satellite health (0=healthy, 1=unhealthy). */
+    /** Satellite health which is set with the GLONASS_HEALTH_STATUS_* constants */
     int svHealth;
 
     /** Message frame time in seconds of the UTC week (tk+nd*86400). */
@@ -93,6 +105,15 @@
     /** Age of current information in days (E). */
     int ageInDays;
 
+    /** Update and validity interval in minutes (P1) **/
+    int updateIntervalMinutes;
+
+    /** Flag to indicate oddness(1) or evenness(0) of update interval (P2). */
+    boolean isOddUpdateInterval;
+
+    /** Flag to indicates if the satellite is a Glonass-M satellitee (M). */
+    boolean isGlonassM;
+
     /** Satellite clock model. */
     GlonassSatelliteClockModel satelliteClockModel;
 
diff --git a/gnss/aidl/android/hardware/gnss/gnss_assistance/GnssAlmanac.aidl b/gnss/aidl/android/hardware/gnss/gnss_assistance/GnssAlmanac.aidl
index 08f3373..f12378b 100644
--- a/gnss/aidl/android/hardware/gnss/gnss_assistance/GnssAlmanac.aidl
+++ b/gnss/aidl/android/hardware/gnss/gnss_assistance/GnssAlmanac.aidl
@@ -40,7 +40,7 @@
      * This is defined Galileo-OS-SIS-ICD-v2.1 section 5.1.10.
      * This is unused for GPS/QZSS/Baidou.
      */
-    int iod;
+    int ioda;
 
     /**
      * Almanac reference week number.
@@ -55,6 +55,12 @@
     int toaSeconds;
 
     /**
+     * Flag to indicate if the satelliteAlmanacs contains complete GNSS
+     * constellation indicated by svid.
+     **/
+    boolean isCompleteAlmanacProvided;
+
+    /**
      * Contains almanac parameters for GPS, QZSS, Galileo, Beidou.
      *
      * For Beidou, this is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.15.
@@ -64,7 +70,7 @@
      */
     @VintfStability
     parcelable GnssSatelliteAlmanac {
-        /** The PRN number of the GNSS satellite. */
+        /** PRN or satellite ID number for the satellite. */
         int svid;
 
         /**
@@ -124,5 +130,5 @@
     }
 
     /** Array of GnssSatelliteAlmanac. */
-    GnssSatelliteAlmanac[] satelliteAlmanac;
+    GnssSatelliteAlmanac[] satelliteAlmanacs;
 }
diff --git a/gnss/aidl/android/hardware/gnss/gnss_assistance/GnssAssistance.aidl b/gnss/aidl/android/hardware/gnss/gnss_assistance/GnssAssistance.aidl
index 4bf6154..21555cb 100644
--- a/gnss/aidl/android/hardware/gnss/gnss_assistance/GnssAssistance.aidl
+++ b/gnss/aidl/android/hardware/gnss/gnss_assistance/GnssAssistance.aidl
@@ -16,6 +16,7 @@
 
 package android.hardware.gnss.gnss_assistance;
 
+import android.hardware.gnss.gnss_assistance.AuxiliaryInformation;
 import android.hardware.gnss.gnss_assistance.BeidouSatelliteEphemeris;
 import android.hardware.gnss.gnss_assistance.GalileoIonosphericModel;
 import android.hardware.gnss.gnss_assistance.GalileoSatelliteEphemeris;
@@ -84,6 +85,9 @@
 
         /** The array of GPS satellite corrections. */
         GnssSatelliteCorrections[] satelliteCorrections;
+
+        /** The auxiliary information. */
+        AuxiliaryInformation auxiliaryInformation;
     }
 
     /** Contains Galileo assistance. */
@@ -112,6 +116,9 @@
 
         /** The array of Galileo satellite corrections. */
         GnssSatelliteCorrections[] satelliteCorrections;
+
+        /** The auxiliary information. */
+        AuxiliaryInformation auxiliaryInformation;
     }
 
     /** Contains Glonass assistance. */
@@ -131,6 +138,9 @@
 
         /** The array of Glonass satellite corrections. */
         GnssSatelliteCorrections[] satelliteCorrections;
+
+        /** The auxiliary information. */
+        AuxiliaryInformation auxiliaryInformation;
     }
 
     /** Contains QZSS assistance. */
@@ -159,6 +169,9 @@
 
         /** The array of QZSS satellite corrections. */
         GnssSatelliteCorrections[] satelliteCorrections;
+
+        /** The auxiliary information. */
+        AuxiliaryInformation auxiliaryInformation;
     }
 
     /** Contains Beidou assistance. */
@@ -187,6 +200,9 @@
 
         /** The array of Beidou satellite corrections. */
         GnssSatelliteCorrections[] satelliteCorrections;
+
+        /** The auxiliary information. */
+        AuxiliaryInformation auxiliaryInformation;
     }
 
     /** GPS assistance. */
diff --git a/gnss/aidl/android/hardware/gnss/gnss_assistance/GpsSatelliteEphemeris.aidl b/gnss/aidl/android/hardware/gnss/gnss_assistance/GpsSatelliteEphemeris.aidl
index ab38030..4be2fcc 100644
--- a/gnss/aidl/android/hardware/gnss/gnss_assistance/GpsSatelliteEphemeris.aidl
+++ b/gnss/aidl/android/hardware/gnss/gnss_assistance/GpsSatelliteEphemeris.aidl
@@ -27,8 +27,8 @@
  */
 @VintfStability
 parcelable GpsSatelliteEphemeris {
-    /** Satellite PRN */
-    int prn;
+    /** PRN or satellite ID number for the GPS satellite. */
+    int svid;
 
     /* Contains information about L2 params. */
     @VintfStability
diff --git a/gnss/aidl/android/hardware/gnss/gnss_assistance/QzssSatelliteEphemeris.aidl b/gnss/aidl/android/hardware/gnss/gnss_assistance/QzssSatelliteEphemeris.aidl
index a5a22d0..6768daf 100644
--- a/gnss/aidl/android/hardware/gnss/gnss_assistance/QzssSatelliteEphemeris.aidl
+++ b/gnss/aidl/android/hardware/gnss/gnss_assistance/QzssSatelliteEphemeris.aidl
@@ -30,8 +30,8 @@
  */
 @VintfStability
 parcelable QzssSatelliteEphemeris {
-    /** Satellite PRN. */
-    int prn;
+    /** PRN or satellite ID number for the Qzss satellite. */
+    int svid;
 
     /** L2 parameters. */
     GpsL2Params gpsL2Params;
diff --git a/gnss/aidl/android/hardware/gnss/gnss_assistance/RealTimeIntegrityModel.aidl b/gnss/aidl/android/hardware/gnss/gnss_assistance/RealTimeIntegrityModel.aidl
index 10a511f..b05176b 100644
--- a/gnss/aidl/android/hardware/gnss/gnss_assistance/RealTimeIntegrityModel.aidl
+++ b/gnss/aidl/android/hardware/gnss/gnss_assistance/RealTimeIntegrityModel.aidl
@@ -16,6 +16,8 @@
 
 package android.hardware.gnss.gnss_assistance;
 
+import android.hardware.gnss.GnssSignalType;
+
 /**
  * Contains the real time integrity status of a GNSS satellite based on
  * notice advisory.
@@ -25,8 +27,8 @@
 @VintfStability
 parcelable RealTimeIntegrityModel {
     /**
-     * Pseudo-random or satellite ID number for the satellite, a.k.a. Space Vehicle (SV), or
-     * OSN number for Glonass. The distinction is made by looking at the constellation field.
+     * The bad signal ID number or OSN number for Glonass.
+     * The distinction is made by looking at the constellation field.
      * Values must be in the range of:
      *
      * - GPS:    1-32
@@ -35,10 +37,13 @@
      * - Galileo: 1-36
      * - Beidou:  1-63
      */
-    int svid;
+    int badSvid;
 
-    /** Indicates whether the satellite is currently usable for navigation. */
-    boolean usable;
+    /**
+     * The signal type of the badSvid, it's set to null array if
+     * all signals on the specific SV are not healthy.
+     */
+    GnssSignalType[] badSignalTypes;
 
     /** UTC timestamp (in seconds) when the advisory was published. */
     long publishDateSeconds;
diff --git a/gnss/aidl/default/GnssDebug.cpp b/gnss/aidl/default/GnssDebug.cpp
index 5ae6edd..185bfe4 100644
--- a/gnss/aidl/default/GnssDebug.cpp
+++ b/gnss/aidl/default/GnssDebug.cpp
@@ -126,9 +126,46 @@
             .ephemerisAgeSeconds = 12,
             .serverPredictionIsAvailable = true,
             .serverPredictionAgeSeconds = 30};
+    SatelliteData satelliteData10 = {
+            .svid = 2,
+            .constellation = GnssConstellationType::GALILEO,
+            .ephemerisType = SatelliteEphemerisType::EPHEMERIS,
+            .ephemerisSource = SatellitePvt::SatelliteEphemerisSource::SERVER_LONG_TERM,
+            .ephemerisHealth = SatelliteEphemerisHealth::GOOD,
+            .ephemerisAgeSeconds = 12,
+            .serverPredictionIsAvailable = true,
+            .serverPredictionAgeSeconds = 30};
+    SatelliteData satelliteData11 = {
+            .svid = 4,
+            .constellation = GnssConstellationType::GALILEO,
+            .ephemerisType = SatelliteEphemerisType::EPHEMERIS,
+            .ephemerisSource = SatellitePvt::SatelliteEphemerisSource::SERVER_LONG_TERM,
+            .ephemerisHealth = SatelliteEphemerisHealth::GOOD,
+            .ephemerisAgeSeconds = 12,
+            .serverPredictionIsAvailable = true,
+            .serverPredictionAgeSeconds = 30};
+    SatelliteData satelliteData12 = {
+            .svid = 10,
+            .constellation = GnssConstellationType::GALILEO,
+            .ephemerisType = SatelliteEphemerisType::EPHEMERIS,
+            .ephemerisSource = SatellitePvt::SatelliteEphemerisSource::SERVER_LONG_TERM,
+            .ephemerisHealth = SatelliteEphemerisHealth::GOOD,
+            .ephemerisAgeSeconds = 12,
+            .serverPredictionIsAvailable = true,
+            .serverPredictionAgeSeconds = 30};
+    SatelliteData satelliteData13 = {
+            .svid = 29,
+            .constellation = GnssConstellationType::GALILEO,
+            .ephemerisType = SatelliteEphemerisType::EPHEMERIS,
+            .ephemerisSource = SatellitePvt::SatelliteEphemerisSource::SERVER_LONG_TERM,
+            .ephemerisHealth = SatelliteEphemerisHealth::GOOD,
+            .ephemerisAgeSeconds = 12,
+            .serverPredictionIsAvailable = true,
+            .serverPredictionAgeSeconds = 30};
     std::vector<SatelliteData> satelliteDataArrayDebug = {
-            satelliteData1, satelliteData2, satelliteData3, satelliteData4, satelliteData5,
-            satelliteData6, satelliteData7, satelliteData8, satelliteData9};
+            satelliteData1,  satelliteData2,  satelliteData3, satelliteData4, satelliteData5,
+            satelliteData6,  satelliteData7,  satelliteData8, satelliteData9, satelliteData10,
+            satelliteData11, satelliteData12, satelliteData13};
     debugData->position = positionDebug;
     debugData->time = timeDebug;
     debugData->satelliteDataArray = satelliteDataArrayDebug;
diff --git a/gnss/aidl/vts/gnss_hal_test.cpp b/gnss/aidl/vts/gnss_hal_test.cpp
index 0dd8b32..f7deb29 100644
--- a/gnss/aidl/vts/gnss_hal_test.cpp
+++ b/gnss/aidl/vts/gnss_hal_test.cpp
@@ -276,35 +276,43 @@
 }
 
 /*
- * FindStrongFrequentBlockableSource:
+ * FindStrongFrequentSource:
  *
- * Search through a GnssSvStatus list for the strongest blockable satellite observed enough times
+ * Search through a GnssSvStatus list for the strongest satellite observed enough times per
+ * constellation
  *
- * returns the strongest source,
- *         or a source with constellation == UNKNOWN if none are found sufficient times
+ * returns the strongest sources for each constellation,
+ *         or an empty vector if none are found sufficient times
  */
-BlocklistedSource GnssHalTest::FindStrongFrequentBlockableSource(
+std::vector<BlocklistedSource> GnssHalTest::FindStrongFrequentSources(
         const std::list<hidl_vec<IGnssCallback_2_1::GnssSvInfo>> sv_info_list,
         const int min_observations) {
-    return FindStrongFrequentBlockableSource(convertToAidl(sv_info_list), min_observations);
+    return FindStrongFrequentSources(convertToAidl(sv_info_list), min_observations);
 }
 
-BlocklistedSource GnssHalTest::FindStrongFrequentBlockableSource(
+bool GnssHalTest::isBlockableConstellation(const GnssConstellationType constellation,
+                                           const bool isCnBuild) {
+    if (constellation == GnssConstellationType::GPS) {
+        return false;
+    }
+    if (isCnBuild && (constellation == GnssConstellationType::BEIDOU)) {
+        // Do not blocklist BDS on CN builds
+        return false;
+    }
+    return true;
+}
+
+std::vector<BlocklistedSource> GnssHalTest::FindStrongFrequentSources(
         const std::list<std::vector<IGnssCallback::GnssSvInfo>> sv_info_list,
         const int min_observations) {
-    std::map<ComparableBlocklistedSource, SignalCounts> mapSignals;
+    ALOGD("Find strongest sv from %d sv_info_list with %d min_observations.",
+          (int)sv_info_list.size(), min_observations);
 
-    bool isCnBuild = Utils::isCnBuild();
-    ALOGD("isCnBuild: %s", isCnBuild ? "true" : "false");
+    std::map<ComparableBlocklistedSource, SignalCounts> mapSignals;
     for (const auto& sv_info_vec : sv_info_list) {
         for (uint32_t iSv = 0; iSv < sv_info_vec.size(); iSv++) {
             const auto& gnss_sv = sv_info_vec[iSv];
-            if ((gnss_sv.svFlag & (int)IGnssCallback::GnssSvFlags::USED_IN_FIX) &&
-                (gnss_sv.constellation != GnssConstellationType::GPS)) {
-                if (isCnBuild && (gnss_sv.constellation == GnssConstellationType::BEIDOU)) {
-                    // Do not blocklist BDS on CN builds
-                    continue;
-                }
+            if (gnss_sv.svFlag & (int)IGnssCallback::GnssSvFlags::USED_IN_FIX) {
                 ComparableBlocklistedSource source;
                 source.id.svid = gnss_sv.svid;
                 source.id.constellation = gnss_sv.constellation;
@@ -326,27 +334,76 @@
         }
     }
 
-    float max_cn0_dbhz_with_sufficient_count = 0.;
-    int total_observation_count = 0;
-    int blocklisted_source_count_observation = 0;
+    // the Cn0 of the strongest SV per constellation
+    std::unordered_map<GnssConstellationType, float> max_cn0_map;
+    // # of total observations of all signals per constellation
+    std::unordered_map<GnssConstellationType, int> total_observation_count_map;
+    // # of observations of the strongest sv per constellation
+    std::unordered_map<GnssConstellationType, int> source_observation_count_map;
+    // the source to blocklist per constellation
+    std::unordered_map<GnssConstellationType, ComparableBlocklistedSource> source_map;
+    // # of signals per constellation
+    std::unordered_map<GnssConstellationType, int> signal_count_map;
 
-    ComparableBlocklistedSource source_to_blocklist;  // initializes to zero = UNKNOWN constellation
     for (auto const& pairSignal : mapSignals) {
-        total_observation_count += pairSignal.second.observations;
-        if ((pairSignal.second.observations >= min_observations) &&
-            (pairSignal.second.max_cn0_dbhz > max_cn0_dbhz_with_sufficient_count)) {
-            source_to_blocklist = pairSignal.first;
-            blocklisted_source_count_observation = pairSignal.second.observations;
-            max_cn0_dbhz_with_sufficient_count = pairSignal.second.max_cn0_dbhz;
+        ComparableBlocklistedSource source = pairSignal.first;
+        total_observation_count_map[source.id.constellation] += pairSignal.second.observations;
+        signal_count_map[source.id.constellation]++;
+        if (pairSignal.second.observations < min_observations) {
+            continue;
+        }
+        if (pairSignal.second.max_cn0_dbhz > max_cn0_map[source.id.constellation]) {
+            source_map[source.id.constellation] = pairSignal.first;
+            source_observation_count_map[source.id.constellation] = pairSignal.second.observations;
+            max_cn0_map[source.id.constellation] = pairSignal.second.max_cn0_dbhz;
         }
     }
-    ALOGD("Among %d observations, chose svid %d, constellation %d, "
-          "with %d observations at %.1f max CNo",
-          total_observation_count, source_to_blocklist.id.svid,
-          (int)source_to_blocklist.id.constellation, blocklisted_source_count_observation,
-          max_cn0_dbhz_with_sufficient_count);
 
-    return source_to_blocklist.id;
+    std::vector<BlocklistedSource> sources;
+    if (aidl_gnss_hal_->getInterfaceVersion() <= 4) {
+        /* For AIDL version <= 4 (launched-in-15 or earlier), only blocklist 1 sv */
+        float max_cn0 = 0;
+        ComparableBlocklistedSource source_to_blocklist;
+        for (auto const& pair : source_map) {
+            GnssConstellationType constellation = pair.first;
+            ComparableBlocklistedSource source = pair.second;
+            if (max_cn0_map[constellation] > max_cn0) {
+                max_cn0 = max_cn0_map[constellation];
+                source_to_blocklist = source;
+            }
+        }
+        if (source_to_blocklist.id.constellation != GnssConstellationType::UNKNOWN) {
+            ALOGD("In constellation %d, among %d observed SVs, svid %d is chosen to blocklist. "
+                  "It has %d observations with max Cn0: %.1f among %d total observations of this "
+                  "constellation.",
+                  (int)source_to_blocklist.id.constellation,
+                  signal_count_map[source_to_blocklist.id.constellation],
+                  source_to_blocklist.id.svid,
+                  source_observation_count_map[source_to_blocklist.id.constellation], max_cn0,
+                  total_observation_count_map[source_to_blocklist.id.constellation]);
+            sources.push_back(source_to_blocklist.id);
+        }
+    } else {
+        /* For AIDL version >= 5 (launched-in-16 or later), blocklist 1 sv per constellation */
+        for (auto const& pair : source_map) {
+            ComparableBlocklistedSource source = pair.second;
+            if (signal_count_map[source.id.constellation] < 4) {
+                // Skip the constellation with a small number of signals
+                // 4 is arbitrarily chosen to avoid affecting constellations with a limited coverage
+                continue;
+            }
+            ALOGD("In constellation %d, among %d observed SVs, svid %d is chosen to blocklist. "
+                  "It has %d observations with max Cn0: %.1f among %d total observations of this "
+                  "constellation.",
+                  (int)source.id.constellation, signal_count_map[source.id.constellation],
+                  source.id.svid, source_observation_count_map[source.id.constellation],
+                  max_cn0_map[source.id.constellation],
+                  total_observation_count_map[source.id.constellation]);
+            sources.push_back(source.id);
+        }
+    }
+
+    return sources;
 }
 
 GnssConstellationType GnssHalTest::startLocationAndGetBlockableConstellation(
diff --git a/gnss/aidl/vts/gnss_hal_test.h b/gnss/aidl/vts/gnss_hal_test.h
index dec5856..c41620a 100644
--- a/gnss/aidl/vts/gnss_hal_test.h
+++ b/gnss/aidl/vts/gnss_hal_test.h
@@ -81,14 +81,17 @@
     std::list<std::vector<android::hardware::gnss::IGnssCallback::GnssSvInfo>> convertToAidl(
             const std::list<hidl_vec<android::hardware::gnss::V2_1::IGnssCallback::GnssSvInfo>>&
                     sv_info_list);
-    android::hardware::gnss::BlocklistedSource FindStrongFrequentBlockableSource(
+    std::vector<android::hardware::gnss::BlocklistedSource> FindStrongFrequentSources(
             const std::list<hidl_vec<android::hardware::gnss::V2_1::IGnssCallback::GnssSvInfo>>
                     sv_info_list,
             const int min_observations);
-    android::hardware::gnss::BlocklistedSource FindStrongFrequentBlockableSource(
+    std::vector<android::hardware::gnss::BlocklistedSource> FindStrongFrequentSources(
             const std::list<std::vector<android::hardware::gnss::IGnssCallback::GnssSvInfo>>
                     sv_info_list,
             const int min_observations);
+    bool isBlockableConstellation(
+            const android::hardware::gnss::GnssConstellationType constellation,
+            const bool isCnBuild);
 
     void checkGnssMeasurementClockFields(const android::hardware::gnss::GnssData& measurement);
     void checkGnssMeasurementFlags(const android::hardware::gnss::GnssMeasurement& measurement);
diff --git a/gnss/aidl/vts/gnss_hal_test_cases.cpp b/gnss/aidl/vts/gnss_hal_test_cases.cpp
index a2e81d1..781476fd 100644
--- a/gnss/aidl/vts/gnss_hal_test_cases.cpp
+++ b/gnss/aidl/vts/gnss_hal_test_cases.cpp
@@ -615,7 +615,7 @@
  * BlocklistIndividualSatellites:
  *
  * 1) Turns on location, waits for 3 locations, ensuring they are valid, and checks corresponding
- * GnssStatus for common satellites (strongest and one other.)
+ * GnssStatus for common satellites (strongest one in each constellation.)
  * 2a & b) Turns off location, and blocklists common satellites.
  * 3) Restart location, wait for 3 locations, ensuring they are valid, and checks corresponding
  * GnssStatus does not use those satellites.
@@ -633,6 +633,7 @@
         return;
     }
 
+    const int kWarmUpLocations = 3;
     const int kLocationsToAwait = 3;
     const int kRetriesToUnBlocklist = 10;
 
@@ -641,7 +642,7 @@
     } else {
         aidl_gnss_cb_->location_cbq_.reset();
     }
-    StartAndCheckLocations(kLocationsToAwait);
+    StartAndCheckLocations(kLocationsToAwait + kWarmUpLocations);
     int location_called_count = (aidl_gnss_hal_->getInterfaceVersion() <= 1)
                                         ? gnss_cb_->location_cbq_.calledCount()
                                         : aidl_gnss_cb_->location_cbq_.calledCount();
@@ -650,37 +651,50 @@
     int sv_info_list_cbq_size = (aidl_gnss_hal_->getInterfaceVersion() <= 1)
                                         ? gnss_cb_->sv_info_list_cbq_.size()
                                         : aidl_gnss_cb_->sv_info_list_cbq_.size();
-    EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait);
+    EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait + kWarmUpLocations);
     ALOGD("Observed %d GnssSvInfo, while awaiting %d Locations (%d received)",
-          sv_info_list_cbq_size, kLocationsToAwait, location_called_count);
+          sv_info_list_cbq_size, kLocationsToAwait + kWarmUpLocations, location_called_count);
 
     /*
-     * Identify strongest SV seen at least kLocationsToAwait -1 times
-     * Why -1?  To avoid test flakiness in case of (plausible) slight flakiness in strongest signal
-     * observability (one epoch RF null)
+     * Identify strongest SV per constellation seen seen at least kLocationsToAwait -1 times.
+     *
+     * Why not (kLocationsToAwait + kWarmUpLocations)?  To avoid test flakiness in case of
+     * (plausible) slight flakiness in strongest signal observability (one epoch RF null)
      */
 
     const int kGnssSvInfoListTimeout = 2;
-    BlocklistedSource source_to_blocklist;
+    std::vector<BlocklistedSource> sources_to_blocklist;
     if (aidl_gnss_hal_->getInterfaceVersion() <= 1) {
+        // Discard kWarmUpLocations sv_info_vec
+        std::list<hidl_vec<IGnssCallback_2_1::GnssSvInfo>> tmp;
+        int count =
+                gnss_cb_->sv_info_list_cbq_.retrieve(tmp, kWarmUpLocations, kGnssSvInfoListTimeout);
+        ASSERT_EQ(count, kWarmUpLocations);
+
+        // Retrieve (sv_info_list_cbq_size - kWarmUpLocations) sv_info_vec
         std::list<hidl_vec<IGnssCallback_2_1::GnssSvInfo>> sv_info_vec_list;
-        int count = gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_vec_list, sv_info_list_cbq_size,
-                                                         kGnssSvInfoListTimeout);
-        ASSERT_EQ(count, sv_info_list_cbq_size);
-        source_to_blocklist =
-                FindStrongFrequentBlockableSource(sv_info_vec_list, kLocationsToAwait - 1);
+        count = gnss_cb_->sv_info_list_cbq_.retrieve(
+                sv_info_vec_list, sv_info_list_cbq_size - kWarmUpLocations, kGnssSvInfoListTimeout);
+        ASSERT_EQ(count, sv_info_list_cbq_size - kWarmUpLocations);
+        sources_to_blocklist = FindStrongFrequentSources(sv_info_vec_list, kLocationsToAwait - 1);
     } else {
+        // Discard kWarmUpLocations sv_info_vec
+        std::list<std::vector<IGnssCallback::GnssSvInfo>> tmp;
+        int count = aidl_gnss_cb_->sv_info_list_cbq_.retrieve(tmp, kWarmUpLocations,
+                                                              kGnssSvInfoListTimeout);
+        ASSERT_EQ(count, kWarmUpLocations);
+
+        // Retrieve (sv_info_list_cbq_size - kWarmUpLocations) sv_info_vec
         std::list<std::vector<IGnssCallback::GnssSvInfo>> sv_info_vec_list;
-        int count = aidl_gnss_cb_->sv_info_list_cbq_.retrieve(
-                sv_info_vec_list, sv_info_list_cbq_size, kGnssSvInfoListTimeout);
-        ASSERT_EQ(count, sv_info_list_cbq_size);
-        source_to_blocklist =
-                FindStrongFrequentBlockableSource(sv_info_vec_list, kLocationsToAwait - 1);
+        count = aidl_gnss_cb_->sv_info_list_cbq_.retrieve(
+                sv_info_vec_list, sv_info_list_cbq_size - kWarmUpLocations, kGnssSvInfoListTimeout);
+        ASSERT_EQ(count, sv_info_list_cbq_size - kWarmUpLocations);
+        sources_to_blocklist = FindStrongFrequentSources(sv_info_vec_list, kLocationsToAwait - 1);
     }
 
-    if (source_to_blocklist.constellation == GnssConstellationType::UNKNOWN) {
-        // Cannot find a blockable satellite. Let the test pass.
-        ALOGD("Cannot find a blockable satellite. Letting the test pass.");
+    if (sources_to_blocklist.empty()) {
+        // Cannot find a satellite to blocklist. Let the test pass.
+        ALOGD("Cannot find a satellite to blocklist. Letting the test pass.");
         return;
     }
 
@@ -693,9 +707,7 @@
     ASSERT_NE(gnss_configuration_hal, nullptr);
 
     std::vector<BlocklistedSource> sources;
-    sources.resize(1);
-    sources[0] = source_to_blocklist;
-
+    sources = sources_to_blocklist;
     status = gnss_configuration_hal->setBlocklist(sources);
     ASSERT_TRUE(status.isOk());
 
@@ -726,26 +738,47 @@
     EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait);
     ALOGD("Observed %d GnssSvInfo, while awaiting %d Locations (%d received)",
           sv_info_list_cbq_size, kLocationsToAwait, location_called_count);
+    bool isCnBuild = Utils::isCnBuild();
     for (int i = 0; i < sv_info_list_cbq_size; ++i) {
         if (aidl_gnss_hal_->getInterfaceVersion() <= 1) {
             hidl_vec<IGnssCallback_2_1::GnssSvInfo> sv_info_vec;
             gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_vec, kGnssSvInfoListTimeout);
             for (uint32_t iSv = 0; iSv < sv_info_vec.size(); iSv++) {
                 auto& gnss_sv = sv_info_vec[iSv];
-                EXPECT_FALSE(
-                        (gnss_sv.v2_0.v1_0.svid == source_to_blocklist.svid) &&
-                        (static_cast<GnssConstellationType>(gnss_sv.v2_0.constellation) ==
-                         source_to_blocklist.constellation) &&
-                        (gnss_sv.v2_0.v1_0.svFlag & IGnssCallback_1_0::GnssSvFlags::USED_IN_FIX));
+                if (!(gnss_sv.v2_0.v1_0.svFlag & IGnssCallback_1_0::GnssSvFlags::USED_IN_FIX)) {
+                    continue;
+                }
+                for (auto const& source : sources_to_blocklist) {
+                    if (isBlockableConstellation(source.constellation, isCnBuild)) {
+                        EXPECT_FALSE((gnss_sv.v2_0.v1_0.svid == source.svid) &&
+                                     (static_cast<GnssConstellationType>(
+                                              gnss_sv.v2_0.constellation) == source.constellation));
+                    } else if ((gnss_sv.v2_0.v1_0.svid == source.svid) &&
+                               (static_cast<GnssConstellationType>(gnss_sv.v2_0.constellation) ==
+                                source.constellation)) {
+                        ALOGW("Found constellation %d, svid %d blocklisted but still used-in-fix.",
+                              source.constellation, source.svid);
+                    }
+                }
             }
         } else {
             std::vector<IGnssCallback::GnssSvInfo> sv_info_vec;
             aidl_gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_vec, kGnssSvInfoListTimeout);
             for (uint32_t iSv = 0; iSv < sv_info_vec.size(); iSv++) {
                 auto& gnss_sv = sv_info_vec[iSv];
-                EXPECT_FALSE((gnss_sv.svid == source_to_blocklist.svid) &&
-                             (gnss_sv.constellation == source_to_blocklist.constellation) &&
-                             (gnss_sv.svFlag & (int)IGnssCallback::GnssSvFlags::USED_IN_FIX));
+                if (!(gnss_sv.svFlag & (int)IGnssCallback::GnssSvFlags::USED_IN_FIX)) {
+                    continue;
+                }
+                for (auto const& source : sources_to_blocklist) {
+                    if (isBlockableConstellation(source.constellation, isCnBuild)) {
+                        EXPECT_FALSE((gnss_sv.svid == source.svid) &&
+                                     (gnss_sv.constellation == source.constellation));
+                    } else if ((gnss_sv.svid == source.svid) &&
+                               (gnss_sv.constellation == source.constellation)) {
+                        ALOGW("Found constellation %d, svid %d blocklisted but still used-in-fix.",
+                              gnss_sv.constellation, gnss_sv.svid);
+                    }
+                }
             }
         }
     }
@@ -795,12 +828,15 @@
                 gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_vec, kGnssSvInfoListTimeout);
                 for (uint32_t iSv = 0; iSv < sv_info_vec.size(); iSv++) {
                     auto& gnss_sv = sv_info_vec[iSv];
-                    if ((gnss_sv.v2_0.v1_0.svid == source_to_blocklist.svid) &&
-                        (static_cast<GnssConstellationType>(gnss_sv.v2_0.constellation) ==
-                         source_to_blocklist.constellation) &&
-                        (gnss_sv.v2_0.v1_0.svFlag & IGnssCallback_1_0::GnssSvFlags::USED_IN_FIX)) {
-                        strongest_sv_is_reobserved = true;
-                        break;
+                    for (auto const& source : sources_to_blocklist) {
+                        if ((gnss_sv.v2_0.v1_0.svid == source.svid) &&
+                            (static_cast<GnssConstellationType>(gnss_sv.v2_0.constellation) ==
+                             source.constellation) &&
+                            (gnss_sv.v2_0.v1_0.svFlag &
+                             IGnssCallback_1_0::GnssSvFlags::USED_IN_FIX)) {
+                            strongest_sv_is_reobserved = true;
+                            break;
+                        }
                     }
                 }
             } else {
@@ -808,11 +844,13 @@
                 aidl_gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_vec, kGnssSvInfoListTimeout);
                 for (uint32_t iSv = 0; iSv < sv_info_vec.size(); iSv++) {
                     auto& gnss_sv = sv_info_vec[iSv];
-                    if ((gnss_sv.svid == source_to_blocklist.svid) &&
-                        (gnss_sv.constellation == source_to_blocklist.constellation) &&
-                        (gnss_sv.svFlag & (int)IGnssCallback::GnssSvFlags::USED_IN_FIX)) {
-                        strongest_sv_is_reobserved = true;
-                        break;
+                    for (auto const& source : sources_to_blocklist) {
+                        if ((gnss_sv.svid == source.svid) &&
+                            (gnss_sv.constellation == source.constellation) &&
+                            (gnss_sv.svFlag & (int)IGnssCallback::GnssSvFlags::USED_IN_FIX)) {
+                            strongest_sv_is_reobserved = true;
+                            break;
+                        }
                     }
                 }
             }
diff --git a/gnss/common/utils/default/Utils.cpp b/gnss/common/utils/default/Utils.cpp
index 8303d93..740bc59 100644
--- a/gnss/common/utils/default/Utils.cpp
+++ b/gnss/common/utils/default/Utils.cpp
@@ -338,16 +338,25 @@
 
 std::vector<GnssSvInfo> Utils::getMockSvInfoList() {
     std::vector<GnssSvInfo> gnssSvInfoList = {
+            // svid in [1, 32] for GPS
             getMockSvInfo(3, GnssConstellationType::GPS, 32.5, 27.5, 59.1, 166.5, kGpsL1FreqHz),
             getMockSvInfo(5, GnssConstellationType::GPS, 27.0, 22.0, 29.0, 56.5, kGpsL1FreqHz),
             getMockSvInfo(17, GnssConstellationType::GPS, 30.5, 25.5, 71.0, 77.0, kGpsL5FreqHz),
             getMockSvInfo(26, GnssConstellationType::GPS, 24.1, 19.1, 28.0, 253.0, kGpsL5FreqHz),
+            // svid in [1, 36] for GAL
+            getMockSvInfo(2, GnssConstellationType::GALILEO, 33.5, 27.5, 59.1, 166.5, kGalE1FreqHz),
+            getMockSvInfo(4, GnssConstellationType::GALILEO, 28.0, 22.0, 29.0, 56.5, kGalE1FreqHz),
+            getMockSvInfo(10, GnssConstellationType::GALILEO, 35.5, 25.5, 71.0, 77.0, kGalE1FreqHz),
+            getMockSvInfo(29, GnssConstellationType::GALILEO, 34.1, 19.1, 28.0, 253.0,
+                          kGalE1FreqHz),
+            // "1 <= svid <= 25 || 93 <= svid <= 106" for GLO
             getMockSvInfo(5, GnssConstellationType::GLONASS, 20.5, 15.5, 11.5, 116.0, kGloG1FreqHz),
             getMockSvInfo(17, GnssConstellationType::GLONASS, 21.5, 16.5, 28.5, 186.0,
                           kGloG1FreqHz),
             getMockSvInfo(18, GnssConstellationType::GLONASS, 28.3, 25.3, 38.8, 69.0, kGloG1FreqHz),
             getMockSvInfo(10, GnssConstellationType::GLONASS, 25.0, 20.0, 66.0, 247.0,
                           kGloG1FreqHz),
+            // "1 <= X <= 14" for IRNSS
             getMockSvInfo(3, GnssConstellationType::IRNSS, 22.0, 19.7, 35.0, 112.0, kIrnssL5FreqHz),
     };
     return gnssSvInfoList;
diff --git a/gnss/common/utils/default/include/Constants.h b/gnss/common/utils/default/include/Constants.h
index 489413e..e6605c4 100644
--- a/gnss/common/utils/default/include/Constants.h
+++ b/gnss/common/utils/default/include/Constants.h
@@ -31,6 +31,7 @@
 const int64_t kMockTimestamp = 1519930775453L;
 const float kGpsL1FreqHz = 1575.42 * 1e6;
 const float kGpsL5FreqHz = 1176.45 * 1e6;
+const float kGalE1FreqHz = 1575.42 * 1e6;
 const float kGloG1FreqHz = 1602.0 * 1e6;
 const float kIrnssL5FreqHz = 1176.45 * 1e6;
 
diff --git a/graphics/composer/aidl/vts/ReadbackVts.cpp b/graphics/composer/aidl/vts/ReadbackVts.cpp
index 9d5928d..b45c8c0 100644
--- a/graphics/composer/aidl/vts/ReadbackVts.cpp
+++ b/graphics/composer/aidl/vts/ReadbackVts.cpp
@@ -37,6 +37,12 @@
     writer.setLayerBlendMode(mDisplay, mLayer, mBlendMode);
     writer.setLayerBrightness(mDisplay, mLayer, mBrightness);
     writer.setLayerDataspace(mDisplay, mLayer, mDataspace);
+    Luts luts{
+            .pfd = ::ndk::ScopedFileDescriptor(dup(mLuts.pfd.get())),
+            .offsets = mLuts.offsets,
+            .lutProperties = mLuts.lutProperties,
+    };
+    writer.setLayerLuts(mDisplay, mLayer, luts);
 }
 
 std::string ReadbackHelper::getColorModeString(ColorMode mode) {
@@ -103,6 +109,24 @@
     layerSettings.geometry.positionTransform = scale * translation;
     layerSettings.whitePointNits = mWhitePointNits;
     layerSettings.sourceDataspace = static_cast<::android::ui::Dataspace>(mDataspace);
+    if (mLuts.pfd.get() >= 0 && mLuts.offsets) {
+        std::vector<int32_t> dimensions;
+        std::vector<int32_t> sizes;
+        std::vector<int32_t> keys;
+        dimensions.reserve(mLuts.lutProperties.size());
+        sizes.reserve(mLuts.lutProperties.size());
+        keys.reserve(mLuts.lutProperties.size());
+
+        for (auto& l : mLuts.lutProperties) {
+            dimensions.emplace_back(static_cast<int32_t>(l.dimension));
+            sizes.emplace_back(static_cast<int32_t>(l.size));
+            keys.emplace_back(static_cast<int32_t>(l.samplingKeys[0]));
+        }
+
+        layerSettings.luts = std::make_shared<::android::gui::DisplayLuts>(
+                ::android::base::unique_fd(dup(mLuts.pfd.get())), *mLuts.offsets, dimensions, sizes,
+                keys);
+    }
 
     return layerSettings;
 }
diff --git a/graphics/composer/aidl/vts/ReadbackVts.h b/graphics/composer/aidl/vts/ReadbackVts.h
index e3b2384..c04e37a 100644
--- a/graphics/composer/aidl/vts/ReadbackVts.h
+++ b/graphics/composer/aidl/vts/ReadbackVts.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
+#include <aidl/android/hardware/graphics/composer3/Luts.h>
 #include <android-base/unique_fd.h>
 #include <android/hardware/graphics/composer3/ComposerClientReader.h>
 #include <android/hardware/graphics/composer3/ComposerClientWriter.h>
@@ -26,6 +27,8 @@
 #include "GraphicsComposerCallback.h"
 #include "VtsComposerClient.h"
 
+using aidl::android::hardware::graphics::composer3::Luts;
+
 namespace aidl::android::hardware::graphics::composer3::vts {
 
 using ::android::renderengine::LayerSettings;
@@ -80,6 +83,7 @@
     void setTransform(Transform transform) { mTransform = transform; }
     void setAlpha(float alpha) { mAlpha = alpha; }
     void setBlendMode(BlendMode blendMode) { mBlendMode = blendMode; }
+    void setLuts(Luts luts) { mLuts = std::move(luts); }
 
     BlendMode getBlendMode() const { return mBlendMode; }
 
@@ -105,6 +109,7 @@
     BlendMode mBlendMode = BlendMode::NONE;
     uint32_t mZOrder = 0;
     Dataspace mDataspace = Dataspace::UNKNOWN;
+    Luts mLuts;
 };
 
 class TestColorLayer : public TestLayer {
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
index 9db8794..f81289a 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
@@ -20,6 +20,7 @@
 #include <aidl/Vintf.h>
 #include <aidl/android/hardware/graphics/common/BufferUsage.h>
 #include <aidl/android/hardware/graphics/composer3/IComposer.h>
+#include <cutils/ashmem.h>
 #include <gtest/gtest.h>
 #include <ui/DisplayId.h>
 #include <ui/DisplayIdentification.h>
@@ -527,6 +528,118 @@
     }
 }
 
+void generateLuts(Luts* luts, LutProperties::Dimension dimension, int32_t size,
+                  LutProperties::SamplingKey key) {
+    size_t bufferSize = dimension == LutProperties::Dimension::ONE_D
+                                ? static_cast<size_t>(size) * sizeof(float)
+                                : static_cast<size_t>(size * size * size) * sizeof(float);
+    int32_t fd = ashmem_create_region("lut_shared_mem", bufferSize);
+    void* ptr = mmap(nullptr, bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+    std::vector<float> buffers(static_cast<size_t>(size), 0.5f);
+    memcpy(ptr, buffers.data(), bufferSize);
+    munmap(ptr, bufferSize);
+    luts->pfd = ndk::ScopedFileDescriptor(fd);
+    luts->offsets = std::vector<int32_t>{0};
+    luts->lutProperties = {LutProperties{dimension, size, {key}}};
+}
+
+TEST_P(GraphicsCompositionTest, Luts) {
+    ASSERT_TRUE(
+            mComposerClient->setClientTargetSlotCount(getPrimaryDisplayId(), kClientTargetSlotCount)
+                    .isOk());
+    const auto& [status, properties] = mComposerClient->getOverlaySupport();
+    if (!status.isOk() && status.getExceptionCode() == EX_SERVICE_SPECIFIC &&
+        status.getServiceSpecificError() == IComposerClient::EX_UNSUPPORTED) {
+        GTEST_SKIP() << "getOverlaySupport is not supported";
+        return;
+    }
+
+    if (!properties.lutProperties) {
+        GTEST_SKIP() << "lutProperties is not supported";
+        return;
+    }
+
+    for (const auto& lutProperties : *properties.lutProperties) {
+        if (!lutProperties) {
+            continue;
+        }
+        auto& l = *lutProperties;
+
+        for (const auto& key : l.samplingKeys) {
+            for (ColorMode mode : mTestColorModes) {
+                EXPECT_TRUE(mComposerClient
+                                    ->setColorMode(getPrimaryDisplayId(), mode,
+                                                   RenderIntent::COLORIMETRIC)
+                                    .isOk());
+
+                bool isSupported;
+                ASSERT_NO_FATAL_FAILURE(isSupported = getHasReadbackBuffer());
+                if (!isSupported) {
+                    GTEST_SUCCEED()
+                            << "Readback not supported or unsupported pixelFormat/dataspace";
+                    return;
+                }
+
+                common::Rect coloredSquare({0, 0, getDisplayWidth(), getDisplayHeight()});
+
+                // expected color for each pixel
+                std::vector<Color> expectedColors(
+                        static_cast<size_t>(getDisplayWidth() * getDisplayHeight()));
+                ReadbackHelper::fillColorsArea(expectedColors, getDisplayWidth(), coloredSquare,
+                                               WHITE);
+
+                auto layer = std::make_shared<TestBufferLayer>(
+                        mComposerClient, *mTestRenderEngine, getPrimaryDisplayId(),
+                        getDisplayWidth(), getDisplayHeight(), PixelFormat::RGBA_8888, *mWriter);
+                layer->setDisplayFrame(coloredSquare);
+                layer->setZOrder(10);
+                layer->setDataspace(Dataspace::SRGB);
+
+                Luts luts;
+                generateLuts(&luts, l.dimension, l.size, key);
+                layer->setLuts(std::move(luts));
+
+                ASSERT_NO_FATAL_FAILURE(layer->setBuffer(expectedColors));
+
+                std::vector<std::shared_ptr<TestLayer>> layers = {layer};
+
+                ReadbackBuffer readbackBuffer(getPrimaryDisplayId(), mComposerClient,
+                                              getDisplayWidth(), getDisplayHeight(), mPixelFormat,
+                                              mDataspace);
+                ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
+
+                writeLayers(layers);
+                ASSERT_TRUE(mReader.takeErrors().empty());
+                mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                         VtsComposerClient::kNoFrameIntervalNs);
+                execute();
+                // if hwc cannot handle and asks for composition change,
+                // just succeed the test
+                if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
+                    GTEST_SUCCEED();
+                    return;
+                }
+
+                auto changedCompositionTypes =
+                        mReader.takeChangedCompositionTypes(getPrimaryDisplayId());
+                ASSERT_TRUE(changedCompositionTypes.empty());
+
+                mWriter->presentDisplay(getPrimaryDisplayId());
+                execute();
+                ASSERT_TRUE(mReader.takeErrors().empty());
+
+                ReadbackHelper::fillColorsArea(expectedColors, getDisplayWidth(), coloredSquare,
+                                               {188.f / 255.f, 188.f / 255.f, 188.f / 255.f, 1.0f});
+
+                ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
+                mTestRenderEngine->setRenderLayers(layers);
+                ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers());
+                ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors));
+            }
+        }
+    }
+}
+
 TEST_P(GraphicsCompositionTest, MixedColorSpaces) {
     ASSERT_TRUE(
             mComposerClient->setClientTargetSlotCount(getPrimaryDisplayId(), kClientTargetSlotCount)
diff --git a/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp b/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
index 3e2f2ac..cfd3173 100644
--- a/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
+++ b/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
@@ -460,8 +460,8 @@
         ASSERT_NE(nullptr, outYCbCr->cr);
     }
 
-    YCbCr getAndroidYCbCr_P010(const native_handle_t* bufferHandle, uint8_t* data) {
-        YCbCr yCbCr_P010;
+    YCbCr getAndroidYCbCr_10bit(const native_handle_t* bufferHandle, uint8_t* data) {
+        YCbCr yCbCr_10bit;
         auto decodeResult = getStandardMetadata<StandardMetadataType::PLANE_LAYOUTS>(bufferHandle);
         if (!decodeResult.has_value()) {
             ADD_FAILURE() << "failed to get plane layout";
@@ -472,12 +472,12 @@
         EXPECT_EQ(1, planeLayouts[0].components.size());
         EXPECT_EQ(2, planeLayouts[1].components.size());
 
-        yCbCr_P010.yCbCr.y = nullptr;
-        yCbCr_P010.yCbCr.cb = nullptr;
-        yCbCr_P010.yCbCr.cr = nullptr;
-        yCbCr_P010.yCbCr.ystride = 0;
-        yCbCr_P010.yCbCr.cstride = 0;
-        yCbCr_P010.yCbCr.chroma_step = 0;
+        yCbCr_10bit.yCbCr.y = nullptr;
+        yCbCr_10bit.yCbCr.cb = nullptr;
+        yCbCr_10bit.yCbCr.cr = nullptr;
+        yCbCr_10bit.yCbCr.ystride = 0;
+        yCbCr_10bit.yCbCr.cstride = 0;
+        yCbCr_10bit.yCbCr.chroma_step = 0;
         int64_t cb_offset = 0;
         int64_t cr_offset = 0;
 
@@ -493,15 +493,15 @@
                 auto type = static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value);
                 switch (type) {
                     case PlaneLayoutComponentType::Y:
-                        // For specs refer:
+                        // For specs refer to:
                         // https://docs.microsoft.com/en-us/windows/win32/medfound/10-bit-and-16-bit-yuv-video-formats
                         EXPECT_EQ(6, planeLayoutComponent.offsetInBits);
-                        EXPECT_EQ(nullptr, yCbCr_P010.yCbCr.y);
+                        EXPECT_EQ(nullptr, yCbCr_10bit.yCbCr.y);
                         EXPECT_EQ(10, planeLayoutComponent.sizeInBits);
                         EXPECT_EQ(16, planeLayout.sampleIncrementInBits);
 
-                        yCbCr_P010.yCbCr.y = tmpData;
-                        yCbCr_P010.yCbCr.ystride = planeLayout.strideInBytes;
+                        yCbCr_10bit.yCbCr.y = tmpData;
+                        yCbCr_10bit.yCbCr.ystride = planeLayout.strideInBytes;
                         break;
 
                     case PlaneLayoutComponentType::CB:
@@ -509,46 +509,46 @@
                         sampleIncrementInBytes = bitsToBytes(planeLayout.sampleIncrementInBits);
                         EXPECT_EQ(4, sampleIncrementInBytes);
 
-                        if (yCbCr_P010.yCbCr.cstride == 0 && yCbCr_P010.yCbCr.chroma_step == 0) {
-                            yCbCr_P010.yCbCr.cstride = planeLayout.strideInBytes;
-                            yCbCr_P010.yCbCr.chroma_step = sampleIncrementInBytes;
+                        if (yCbCr_10bit.yCbCr.cstride == 0 && yCbCr_10bit.yCbCr.chroma_step == 0) {
+                            yCbCr_10bit.yCbCr.cstride = planeLayout.strideInBytes;
+                            yCbCr_10bit.yCbCr.chroma_step = sampleIncrementInBytes;
                         } else {
-                            EXPECT_EQ(yCbCr_P010.yCbCr.cstride, planeLayout.strideInBytes);
-                            EXPECT_EQ(yCbCr_P010.yCbCr.chroma_step, sampleIncrementInBytes);
+                            EXPECT_EQ(yCbCr_10bit.yCbCr.cstride, planeLayout.strideInBytes);
+                            EXPECT_EQ(yCbCr_10bit.yCbCr.chroma_step, sampleIncrementInBytes);
                         }
 
-                        if (yCbCr_P010.horizontalSubSampling == 0 &&
-                            yCbCr_P010.verticalSubSampling == 0) {
-                            yCbCr_P010.horizontalSubSampling = planeLayout.horizontalSubsampling;
-                            yCbCr_P010.verticalSubSampling = planeLayout.verticalSubsampling;
+                        if (yCbCr_10bit.horizontalSubSampling == 0 &&
+                            yCbCr_10bit.verticalSubSampling == 0) {
+                            yCbCr_10bit.horizontalSubSampling = planeLayout.horizontalSubsampling;
+                            yCbCr_10bit.verticalSubSampling = planeLayout.verticalSubsampling;
                         } else {
-                            EXPECT_EQ(yCbCr_P010.horizontalSubSampling,
+                            EXPECT_EQ(yCbCr_10bit.horizontalSubSampling,
                                       planeLayout.horizontalSubsampling);
-                            EXPECT_EQ(yCbCr_P010.verticalSubSampling,
+                            EXPECT_EQ(yCbCr_10bit.verticalSubSampling,
                                       planeLayout.verticalSubsampling);
                         }
 
                         if (type == PlaneLayoutComponentType::CB) {
-                            EXPECT_EQ(nullptr, yCbCr_P010.yCbCr.cb);
-                            yCbCr_P010.yCbCr.cb = tmpData;
+                            EXPECT_EQ(nullptr, yCbCr_10bit.yCbCr.cb);
+                            yCbCr_10bit.yCbCr.cb = tmpData;
                             cb_offset = planeLayoutComponent.offsetInBits;
                         } else {
-                            EXPECT_EQ(nullptr, yCbCr_P010.yCbCr.cr);
-                            yCbCr_P010.yCbCr.cr = tmpData;
+                            EXPECT_EQ(nullptr, yCbCr_10bit.yCbCr.cr);
+                            yCbCr_10bit.yCbCr.cr = tmpData;
                             cr_offset = planeLayoutComponent.offsetInBits;
                         }
                         break;
                     default:
                         break;
-                };
+                }
             }
         }
 
         EXPECT_EQ(cb_offset + bytesToBits(2), cr_offset);
-        EXPECT_NE(nullptr, yCbCr_P010.yCbCr.y);
-        EXPECT_NE(nullptr, yCbCr_P010.yCbCr.cb);
-        EXPECT_NE(nullptr, yCbCr_P010.yCbCr.cr);
-        return yCbCr_P010;
+        EXPECT_NE(nullptr, yCbCr_10bit.yCbCr.y);
+        EXPECT_NE(nullptr, yCbCr_10bit.yCbCr.cb);
+        EXPECT_NE(nullptr, yCbCr_10bit.yCbCr.cr);
+        return yCbCr_10bit;
     }
 };
 
@@ -1131,7 +1131,7 @@
                                                      region, -1, (void**)&data));
 
     YCbCr yCbCr;
-    ASSERT_NO_FATAL_FAILURE(yCbCr = getAndroidYCbCr_P010(*handle, data));
+    ASSERT_NO_FATAL_FAILURE(yCbCr = getAndroidYCbCr_10bit(*handle, data));
 
     constexpr uint32_t kCbCrSubSampleFactor = 2;
     ASSERT_EQ(kCbCrSubSampleFactor, yCbCr.horizontalSubSampling);
@@ -1153,6 +1153,54 @@
     }
 }
 
+TEST_P(GraphicsMapperStableCTests, Lock_YCBCR_P210) {
+    BufferDescriptorInfo info{
+        .name = {"VTS_TEMP"},
+        .width = 64,
+        .height = 64,
+        .layerCount = 1,
+        .format = PixelFormat::YCBCR_P210,
+        .usage = BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN,
+        .reservedSize = 0,
+    };
+    auto buffer = allocate(info);
+    if (!buffer) {
+        ASSERT_FALSE(isSupported(info));
+        GTEST_SUCCEED() << "YCBCR_P210 format is unsupported";
+        return;
+    }
+
+    // lock buffer for writing
+    const ARect region{0, 0, info.width, info.height};
+    auto handle = buffer->import();
+    uint8_t *data = nullptr;
+    ASSERT_EQ(AIMAPPER_ERROR_NONE,
+              mapper()->v5.lock(*handle, static_cast<int64_t>(info.usage), region,
+                                -1, (void **)&data));
+
+    YCbCr yCbCr;
+    ASSERT_NO_FATAL_FAILURE(yCbCr = getAndroidYCbCr_10bit(*handle, data));
+
+    constexpr uint32_t kCbCrSubSampleFactor = 2;
+    ASSERT_EQ(kCbCrSubSampleFactor, yCbCr.horizontalSubSampling);
+    ASSERT_EQ(1, yCbCr.verticalSubSampling);
+
+    ASSERT_EQ(0, info.height % 2);
+
+    // fill the data
+    fillYCbCrData(yCbCr.yCbCr, info.width, info.height,
+                  yCbCr.horizontalSubSampling, yCbCr.verticalSubSampling);
+    // verify the YCbCr data
+    verifyYCbCrData(yCbCr.yCbCr, info.width, info.height,
+                    yCbCr.horizontalSubSampling, yCbCr.verticalSubSampling);
+
+    int releaseFence = -1;
+    ASSERT_EQ(AIMAPPER_ERROR_NONE, mapper()->v5.unlock(*handle, &releaseFence));
+    if (releaseFence != -1) {
+        close(releaseFence);
+    }
+}
+
 TEST_P(GraphicsMapperStableCTests, LockBadAccessRegion) {
     auto buffer = allocateGeneric();
     ASSERT_NE(nullptr, buffer);
diff --git a/neuralnetworks/aidl/Android.bp b/neuralnetworks/aidl/Android.bp
index 45b34e6..e7583aa 100644
--- a/neuralnetworks/aidl/Android.bp
+++ b/neuralnetworks/aidl/Android.bp
@@ -33,7 +33,6 @@
             apex_available: [
                 "//apex_available:platform",
                 "com.android.neuralnetworks",
-                "test_com.android.neuralnetworks",
             ],
             min_sdk_version: "30",
         },
diff --git a/nfc/aidl/vts/functional/Android.bp b/nfc/aidl/vts/functional/Android.bp
index f97405e..8852f6c 100644
--- a/nfc/aidl/vts/functional/Android.bp
+++ b/nfc/aidl/vts/functional/Android.bp
@@ -58,12 +58,12 @@
         "CondVar.cpp",
     ],
     include_dirs: [
-        "system/nfc/src/gki/common",
-        "system/nfc/src/gki/ulinux",
-        "system/nfc/src/include",
-        "system/nfc/src/nfa/include",
-        "system/nfc/src/nfc/include",
-        "system/nfc/utils/include",
+        "packages/modules/Nfc/libnfc-nci/src/gki/common",
+        "packages/modules/Nfc/libnfc-nci/src/gki/ulinux",
+        "packages/modules/Nfc/libnfc-nci/src/include",
+        "packages/modules/Nfc/libnfc-nci/src/nfa/include",
+        "packages/modules/Nfc/libnfc-nci/src/nfc/include",
+        "packages/modules/Nfc/libnfc-nci/utils/include",
     ],
     shared_libs: [
         "liblog",
diff --git a/power/OWNERS b/power/OWNERS
index 13895bd..9576e14 100644
--- a/power/OWNERS
+++ b/power/OWNERS
@@ -1,3 +1,2 @@
 # Bug component: 826709
 file:platform/frameworks/base:/ADPF_OWNERS
-wvw@google.com
diff --git a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/SupportInfo.aidl b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/SupportInfo.aidl
index c90125c..d2fcae2 100644
--- a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/SupportInfo.aidl
+++ b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/SupportInfo.aidl
@@ -55,5 +55,10 @@
     boolean isGpuSupported;
     int cpuMinIntervalMillis;
     int gpuMinIntervalMillis;
+    int cpuMinCalculationWindowMillis = 50;
+    int cpuMaxCalculationWindowMillis = 10000;
+    int gpuMinCalculationWindowMillis = 50;
+    int gpuMaxCalculationWindowMillis = 10000;
+    int cpuMaxTidCount = 5;
   }
 }
diff --git a/power/aidl/android/hardware/power/CpuHeadroomParams.aidl b/power/aidl/android/hardware/power/CpuHeadroomParams.aidl
index fab8f43..83a46ae 100644
--- a/power/aidl/android/hardware/power/CpuHeadroomParams.aidl
+++ b/power/aidl/android/hardware/power/CpuHeadroomParams.aidl
@@ -35,9 +35,8 @@
     CalculationType calculationType = CalculationType.MIN;
 
     /**
-     * The calculation rolling window size in milliseconds.
-     * The device should support a superset of [50, 10000] and try to use the closest feasible
-     * window size to the provided value param.
+     * The device should support the range specified in SupportInfo#HeadroomSupportInfo and try to
+     * use the closest feasible window size to the provided value param.
      */
     int calculationWindowMillis = 1000;
 
@@ -50,6 +49,8 @@
      * This should handle all the cases including but not limited to core affinity and app cpuset
      * that change the available CPU cores for the caller. And the HAL should check that the TIDs
      * have the same core affinity.
+     *
+     * The device should support the maximum TID count specified SupportInfo#HeadroomSupportInfo.
      */
     int[] tids;
 }
diff --git a/power/aidl/android/hardware/power/GpuHeadroomParams.aidl b/power/aidl/android/hardware/power/GpuHeadroomParams.aidl
index 68848d8..2d64bd3 100644
--- a/power/aidl/android/hardware/power/GpuHeadroomParams.aidl
+++ b/power/aidl/android/hardware/power/GpuHeadroomParams.aidl
@@ -35,8 +35,8 @@
     CalculationType calculationType = CalculationType.MIN;
 
     /**
-     * The device should support a superset of [50, 10000] and try to use the closest feasible
-     * window size to the provided value param.
+     * The device should support the range specified in SupportInfo#HeadroomSupportInfo and try to
+     * use the closest feasible window size to the provided value param.
      */
     int calculationWindowMillis = 1000;
 }
diff --git a/power/aidl/android/hardware/power/SupportInfo.aidl b/power/aidl/android/hardware/power/SupportInfo.aidl
index 55287cb..011dc18 100644
--- a/power/aidl/android/hardware/power/SupportInfo.aidl
+++ b/power/aidl/android/hardware/power/SupportInfo.aidl
@@ -139,5 +139,39 @@
          * than the interval.
          */
         int gpuMinIntervalMillis;
+
+        /**
+         * Minimum calculation window size for getCpuHeadroom in milliseconds.
+         *
+         * This should be no larger than 50ms.
+         */
+        int cpuMinCalculationWindowMillis = 50;
+
+        /**
+         * Maximum calculation window size for getCpuHeadroom in milliseconds.
+         *
+         * This should be no smaller than 10000ms.
+         */
+        int cpuMaxCalculationWindowMillis = 10000;
+
+        /**
+         * Minimum calculation window size for getGpuHeadroom in milliseconds.
+         *
+         * This should be no larger than 50ms.
+         */
+        int gpuMinCalculationWindowMillis = 50;
+
+        /**
+         * Maximum calculation window size for getGpuHeadroom in milliseconds.
+         *
+         * This should be no smaller than 10000ms.
+         */
+        int gpuMaxCalculationWindowMillis = 10000;
+
+        /**
+         * Maximum number of TIDs this device can support.
+         * This should be no smaller than 5.
+         */
+        int cpuMaxTidCount = 5;
     }
 }
diff --git a/radio/1.0/vts/functional/radio_hidl_hal_data.cpp b/radio/1.0/vts/functional/radio_hidl_hal_data.cpp
index e89f4ee..38cb33b 100644
--- a/radio/1.0/vts/functional/radio_hidl_hal_data.cpp
+++ b/radio/1.0/vts/functional/radio_hidl_hal_data.cpp
@@ -16,7 +16,6 @@
 
 #include <android-base/logging.h>
 #include <android/hardware/radio/1.2/IRadio.h>
-#include <gtest/gtest.h>
 #include <radio_hidl_hal_utils_v1_0.h>
 
 using namespace ::android::hardware::radio::V1_0;
@@ -73,16 +72,11 @@
             CellIdentityTdscdma cit = cellIdentities.cellIdentityTdscdma[0];
             hidl_mcc = cit.mcc;
             hidl_mnc = cit.mnc;
-        } else if (cellInfoType == CellInfoType::CDMA) {
+        } else {
             // CellIndentityCdma has no mcc and mnc.
             EXPECT_EQ(CellInfoType::CDMA, cellInfoType);
             EXPECT_EQ(1, cellIdentities.cellIdentityCdma.size());
             checkMccMnc = false;
-        } else {
-            // This test can be skipped for newer networks if a new RAT (e.g. 5g) that was not
-            // supported in this version is added to the response from a modem that supports a new
-            // version of this interface.
-            GTEST_SKIP() << "Exempt from 1.0 test: camped on a new network:" << (int)cellInfoType;
         }
 
         // Check only one CellIdentity is size 1, and others must be 0.
diff --git a/radio/1.2/vts/functional/radio_hidl_hal_api.cpp b/radio/1.2/vts/functional/radio_hidl_hal_api.cpp
index 51ca967..2bce2f9 100644
--- a/radio/1.2/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.2/vts/functional/radio_hidl_hal_api.cpp
@@ -807,16 +807,11 @@
             cellIdentities.cellIdentityTdscdma[0];
         hidl_mcc = cit.base.mcc;
         hidl_mnc = cit.base.mnc;
-    } else if (cellInfoType == CellInfoType::CDMA) {
+    } else {
         // CellIndentityCdma has no mcc and mnc.
         EXPECT_EQ(CellInfoType::CDMA, cellInfoType);
         EXPECT_EQ(1, cellIdentities.cellIdentityCdma.size());
         checkMccMnc = false;
-    } else {
-        // This test can be skipped for newer networks if a new RAT (e.g. 5g) that was not
-        // supported in this version is added to the response from a modem that supports a new
-        // version of this interface.
-        GTEST_SKIP() << "Exempt from 1.2 test: camped on a new network:" << (int)cellInfoType;
     }
 
     // Check only one CellIdentity is size 1, and others must be 0.
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 9f530b3..f909676 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
@@ -239,18 +239,13 @@
     EXPECT_EQ(std::cv_status::no_timeout, wait());
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
-    if (getRadioHalCapabilities()) {
-        ASSERT_TRUE(CheckAnyOfErrors(
-                radioRsp_v1_6->rspInfo.error,
-                {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
-    } else {
-        ASSERT_TRUE(
-                CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
-                                 {::android::hardware::radio::V1_6::RadioError::NONE,
-                                  ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
-                                  ::android::hardware::radio::V1_6::RadioError::INTERNAL_ERR,
-                                  ::android::hardware::radio::V1_6::RadioError::MODEM_ERR}));
-    }
+    ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_v1_6->rspInfo.error,
+            {::android::hardware::radio::V1_6::RadioError::NONE,
+             ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED,
+             ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+             ::android::hardware::radio::V1_6::RadioError::INTERNAL_ERR,
+             ::android::hardware::radio::V1_6::RadioError::MODEM_ERR}));
 }
 
 /*
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/NasProtocolMessage.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/NasProtocolMessage.aidl
index 4fbc802..870cee1 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/NasProtocolMessage.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/NasProtocolMessage.aidl
@@ -47,4 +47,6 @@
   CM_REESTABLISHMENT_REQUEST = 9,
   CM_SERVICE_REQUEST = 10,
   IMSI_DETACH_INDICATION = 11,
+  THREAT_IDENTIFIER_FALSE = 12,
+  THREAT_IDENTIFIER_TRUE = 13,
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio/current/android/hardware/radio/RadioAccessFamily.aidl b/radio/aidl/aidl_api/android.hardware.radio/current/android/hardware/radio/RadioAccessFamily.aidl
index 471916f..f44385a 100644
--- a/radio/aidl/aidl_api/android.hardware.radio/current/android/hardware/radio/RadioAccessFamily.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio/current/android/hardware/radio/RadioAccessFamily.aidl
@@ -80,5 +80,4 @@
    */
   LTE_CA = (1 << android.hardware.radio.RadioTechnology.LTE_CA) /* 524288 */,
   NR = (1 << android.hardware.radio.RadioTechnology.NR) /* 1048576 */,
-  NB_IOT_NTN = (1 << android.hardware.radio.RadioTechnology.NB_IOT_NTN) /* 2097152 */,
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio/current/android/hardware/radio/RadioTechnology.aidl b/radio/aidl/aidl_api/android.hardware.radio/current/android/hardware/radio/RadioTechnology.aidl
index 201e694..7aae601 100644
--- a/radio/aidl/aidl_api/android.hardware.radio/current/android/hardware/radio/RadioTechnology.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio/current/android/hardware/radio/RadioTechnology.aidl
@@ -80,5 +80,4 @@
    */
   LTE_CA,
   NR,
-  NB_IOT_NTN,
 }
diff --git a/radio/aidl/android/hardware/radio/RadioAccessFamily.aidl b/radio/aidl/android/hardware/radio/RadioAccessFamily.aidl
index 8c10bb9..9588ed9 100644
--- a/radio/aidl/android/hardware/radio/RadioAccessFamily.aidl
+++ b/radio/aidl/android/hardware/radio/RadioAccessFamily.aidl
@@ -55,8 +55,4 @@
      * 5G NR. This is only use in 5G Standalone mode.
      */
     NR = 1 << RadioTechnology.NR,
-    /**
-     * 3GPP NB-IOT (Narrowband Internet of Things) over Non-Terrestrial-Networks technology.
-     */
-    NB_IOT_NTN = 1 << RadioTechnology.NB_IOT_NTN,
 }
diff --git a/radio/aidl/android/hardware/radio/RadioTechnology.aidl b/radio/aidl/android/hardware/radio/RadioTechnology.aidl
index 843bfd0..de93a2b 100644
--- a/radio/aidl/android/hardware/radio/RadioTechnology.aidl
+++ b/radio/aidl/android/hardware/radio/RadioTechnology.aidl
@@ -62,8 +62,4 @@
      * 5G NR. This is only used in 5G Standalone mode.
      */
     NR,
-    /**
-     * 3GPP NB-IOT (Narrowband Internet of Things) over Non-Terrestrial-Networks technology.
-     */
-    NB_IOT_NTN,
 }
diff --git a/radio/aidl/android/hardware/radio/network/NasProtocolMessage.aidl b/radio/aidl/android/hardware/radio/network/NasProtocolMessage.aidl
index 5a23661..f96a884 100644
--- a/radio/aidl/android/hardware/radio/network/NasProtocolMessage.aidl
+++ b/radio/aidl/android/hardware/radio/network/NasProtocolMessage.aidl
@@ -21,6 +21,9 @@
  * generation is noted for each message type. Sample spec references are provided, but generally
  * only reference one network generation's spec.
  *
+ * The exceptions to this rule are THREAT_IDENTIFIER_FALSE and THREAT_IDENTIIFER_TRUE, which are
+ * included to accommodate threat ranking of disclosures based on modem logic.
+ *
  * @hide
  */
 @VintfStability
@@ -64,5 +67,11 @@
     CM_SERVICE_REQUEST = 10,
     // Reference: 3GPP TS 24.008 9.2.14
     // Applies to 2g and 3g networks. Used for circuit-switched detach.
-    IMSI_DETACH_INDICATION = 11
+    IMSI_DETACH_INDICATION = 11,
+    // Vendor-specific enumeration to identify a disclosure as potentially benign.
+    // Enables vendors to semantically define disclosures based on their own classification logic.
+    THREAT_IDENTIFIER_FALSE = 12,
+    // Vendor-specific enumeration to identify a disclosure as potentially harmful.
+    // Enables vendors to semantically define disclosures based on their own classification logic.
+    THREAT_IDENTIFIER_TRUE = 13
 }
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
index 2945dab..e6d2fdf 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -45,6 +45,9 @@
   void deleteAllKeys();
   void destroyAttestationIds();
   android.hardware.security.keymint.BeginResult begin(in android.hardware.security.keymint.KeyPurpose purpose, in byte[] keyBlob, in android.hardware.security.keymint.KeyParameter[] params, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken);
+  /**
+   * @deprecated Method has never been used due to design limitations
+   */
   void deviceLocked(in boolean passwordOnly, in @nullable android.hardware.security.secureclock.TimeStampToken timestampToken);
   void earlyBootEnded();
   byte[] convertStorageKeyToEphemeral(in byte[] storageKeyBlob);
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index b57dd8a..cafec70 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -826,6 +826,7 @@
      *
      * @param passwordOnly N/A due to the deprecation
      * @param timestampToken N/A due to the deprecation
+     * @deprecated Method has never been used due to design limitations
      */
     void deviceLocked(in boolean passwordOnly, in @nullable TimeStampToken timestampToken);
 
@@ -964,10 +965,11 @@
      * IKeyMintDevice must ignore KeyParameters with tags not included in the following list:
      *
      * o Tag::MODULE_HASH: holds a hash that must be included in attestations in the moduleHash
-     *   field of the software enforced authorization list. If Tag::MODULE_HASH is included in more
-     *   than one setAdditionalAttestationInfo call, the implementation should compare the initial
-     *   KeyParamValue with the more recent one. If they differ, the implementation should fail with
-     *   ErrorCode::MODULE_HASH_ALREADY_SET. If they are the same, no action needs to be taken.
+     *   field of the software enforced authorization list.
+     *
+     * @return error ErrorCode::MODULE_HASH_ALREADY_SET if this is not the first time
+     *         setAdditionalAttestationInfo is called with Tag::MODULE_HASH, and the associated
+     *         KeyParamValue of the current call doesn't match the KeyParamValue of the first call.
      */
     void setAdditionalAttestationInfo(in KeyParameter[] info);
 }
diff --git a/security/keymint/aidl/vts/functional/Android.bp b/security/keymint/aidl/vts/functional/Android.bp
index 1414220..da3427a 100644
--- a/security/keymint/aidl/vts/functional/Android.bp
+++ b/security/keymint/aidl/vts/functional/Android.bp
@@ -37,6 +37,7 @@
         "libcrypto",
         "libbase",
         "libgatekeeper",
+        "libvendorsupport",
         "packagemanager_aidl-cpp",
     ],
     static_libs: [
diff --git a/security/keymint/aidl/vts/functional/BootloaderStateTest.cpp b/security/keymint/aidl/vts/functional/BootloaderStateTest.cpp
index b41da3f..a799ab1 100644
--- a/security/keymint/aidl/vts/functional/BootloaderStateTest.cpp
+++ b/security/keymint/aidl/vts/functional/BootloaderStateTest.cpp
@@ -27,6 +27,7 @@
 #include <libavb/libavb.h>
 #include <libavb_user/avb_ops_user.h>
 #include <remote_prov/remote_prov_utils.h>
+#include <vendorsupport/api_level.h>
 
 #include "KeyMintAidlTestBase.h"
 
@@ -98,7 +99,7 @@
 // Check that the attested Verified Boot key is 32 bytes of zeroes since the bootloader is unlocked.
 TEST_P(BootloaderStateTest, VerifiedBootKeyAllZeroes) {
     // Gate this test to avoid waiver issues.
-    if (get_vsr_api_level() <= __ANDROID_API_V__) {
+    if (get_vendor_api_level() <= AVendorSupport_getVendorApiLevelOf(__ANDROID_API_V__)) {
         return;
     }
 
@@ -141,13 +142,13 @@
     avb_slot_verify_data_calculate_vbmeta_digest(avbSlotData, AVB_DIGEST_TYPE_SHA256,
                                                  sha256Digest.data());
 
-    if (get_vsr_api_level() >= __ANDROID_API_V__) {
+    if (get_vendor_api_level() >= AVendorSupport_getVendorApiLevelOf(__ANDROID_API_V__)) {
         ASSERT_TRUE(attestedVbmetaDigest_ == sha256Digest)
                 << "Attested VBMeta digest (" << bin2hex(attestedVbmetaDigest_)
                 << ") does not match the expected SHA-256 digest (" << bin2hex(sha256Digest)
                 << ").";
     } else {
-        // Prior to VSR-V, there was no MUST requirement for the algorithm used by the bootloader
+        // Prior to VSR-15, there was no MUST requirement for the algorithm used by the bootloader
         // to calculate the VBMeta digest. However, the only two supported options are SHA-256 and
         // SHA-512, so we expect the attested VBMeta digest to match one of these.
         vector<uint8_t> sha512Digest(AVB_SHA512_DIGEST_SIZE);
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 09446ce..06e0f58 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -35,6 +35,7 @@
 #include <openssl/evp.h>
 #include <openssl/mem.h>
 #include <remote_prov/remote_prov_utils.h>
+#include <vendorsupport/api_level.h>
 
 #include <keymaster/cppcose/cppcose.h>
 #include <keymint_support/key_param_output.h>
@@ -1434,12 +1435,11 @@
 }
 
 bool KeyMintAidlTestBase::IsRkpSupportRequired() const {
-    // This is technically not a match to the requirements for S chipsets,
-    // however when S shipped there was a bug in the test that skipped the
-    // tests if KeyMint 2 was not on the system. So we allowed many chipests
-    // to ship without RKP support. In T we hardened the requirements around
-    // support for RKP, so relax the test to match.
-    return get_vsr_api_level() >= __ANDROID_API_T__;
+    // This is technically weaker than the VSR-12 requirements, but when
+    // Android 12 shipped, there was a bug that skipped the tests if KeyMint
+    // 2 was not present. As a result, many chipsets were allowed to ship
+    // without RKP support. The RKP requirements were hardened in VSR-13.
+    return get_vendor_api_level() >= __ANDROID_API_T__;
 }
 
 vector<uint32_t> KeyMintAidlTestBase::ValidKeySizes(Algorithm algorithm) {
@@ -1690,11 +1690,11 @@
                                                  vector<uint8_t>* key_blob,
                                                  vector<KeyCharacteristics>* key_characteristics,
                                                  vector<Certificate>* cert_chain) {
-    // The original specification for KeyMint v1 required ATTEST_KEY not be combined
-    // with any other key purpose, but the original VTS tests incorrectly did exactly that.
-    // This means that a device that launched prior to Android T (API level 33) may
-    // accept or even require KeyPurpose::SIGN too.
-    if (get_vsr_api_level() < __ANDROID_API_T__) {
+    // The original specification for KeyMint v1 (introduced in Android 12) required ATTEST_KEY not
+    // be combined with any other key purpose, but the original VTS-12 tests incorrectly did exactly
+    // that. The tests were fixed in VTS-13 (vendor API level 33). This means that devices with
+    // vendor API level < 33 may accept or even require KeyPurpose::SIGN too.
+    if (get_vendor_api_level() < __ANDROID_API_T__) {
         AuthorizationSet key_desc_plus_sign = key_desc;
         key_desc_plus_sign.push_back(TAG_PURPOSE, KeyPurpose::SIGN);
 
@@ -1819,13 +1819,19 @@
     OPENSSL_free(cert_issuer);
 }
 
-int get_vsr_api_level() {
+int get_vendor_api_level() {
+    // Android 13+ builds have the `ro.vendor.api_level` system property. See
+    // https://source.android.com/docs/core/architecture/api-flags#determine_vendor_api_level_android_13.
     int vendor_api_level = ::android::base::GetIntProperty("ro.vendor.api_level", -1);
     if (vendor_api_level != -1) {
         return vendor_api_level;
     }
 
-    // Android S and older devices do not define ro.vendor.api_level
+    // Android 12 builds have the `ro.board.api_level` and `ro.board.first_api_level` system
+    // properties, which are only expected to be populated for GRF SoCs on Android 12 builds. Note
+    // that they are populated automatically by the build system starting in Android 15, but we use
+    // `ro.vendor.api_level` on such builds (see above). For details, see
+    // https://docs.partner.android.com/gms/building/integrating/extending-os-upgrade-support-windows#new-system-properties.
     vendor_api_level = ::android::base::GetIntProperty("ro.board.api_level", -1);
     if (vendor_api_level == -1) {
         vendor_api_level = ::android::base::GetIntProperty("ro.board.first_api_level", -1);
@@ -1837,11 +1843,12 @@
         EXPECT_NE(product_api_level, -1) << "Could not find ro.build.version.sdk";
     }
 
-    // VSR API level is the minimum of vendor_api_level and product_api_level.
-    if (vendor_api_level == -1 || vendor_api_level > product_api_level) {
+    // If the `ro.board.api_level` and `ro.board.first_api_level` properties aren't populated, it
+    // means the build doesn't have a GRF SoC, so the product API level should be used.
+    if (vendor_api_level == -1) {
         return product_api_level;
     }
-    return vendor_api_level;
+    return std::min(product_api_level, vendor_api_level);
 }
 
 bool is_gsi_image() {
@@ -1908,13 +1915,13 @@
         }
     }
 
-    if (get_vsr_api_level() > __ANDROID_API_V__) {
+    if (get_vendor_api_level() > AVendorSupport_getVendorApiLevelOf(__ANDROID_API_V__)) {
         // The Verified Boot key field should be exactly 32 bytes since it
         // contains the SHA-256 hash of the key on locked devices or 32 bytes
         // of zeroes on unlocked devices. This wasn't checked for earlier
-        // versions of the KeyMint HAL, so only only be strict for VSR-16+.
+        // versions of the KeyMint HAL, so we version-gate the strict check.
         EXPECT_EQ(verified_boot_key.size(), 32);
-    } else if (get_vsr_api_level() == __ANDROID_API_V__) {
+    } else if (get_vendor_api_level() == AVendorSupport_getVendorApiLevelOf(__ANDROID_API_V__)) {
         // The Verified Boot key field should be:
         //   - Exactly 32 bytes on locked devices since it should contain
         //     the SHA-256 hash of the key, or
@@ -1923,7 +1930,7 @@
         //     specification).
         // Thus, we can't check for strict equality in case unlocked devices
         // report values with less than 32 bytes. This wasn't checked for
-        // earlier versions of the KeyMint HAL, so only check on VSR-15.
+        // earlier versions of the KeyMint HAL, so we version-gate the check.
         EXPECT_LE(verified_boot_key.size(), 32);
     }
 
@@ -2415,7 +2422,7 @@
     } else if (result == ErrorCode::INVALID_TAG) {
         // Depending on the situation, other error codes may be acceptable.  First, allow older
         // implementations to use INVALID_TAG.
-        ASSERT_FALSE(get_vsr_api_level() > __ANDROID_API_T__)
+        ASSERT_FALSE(get_vendor_api_level() > __ANDROID_API_T__)
                 << "It is a specification violation for INVALID_TAG to be returned due to ID "
                 << "mismatch in a Device ID Attestation call. INVALID_TAG is only intended to "
                 << "be used for a case where updateAad() is called after update(). As of "
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index 1c12136..6c327bb 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -406,8 +406,8 @@
     add_tag(tags, ttag, ::android::base::GetProperty(prop, /* default= */ ""));
 }
 
-// Return the VSR API level for this device.
-int get_vsr_api_level();
+// Return the vendor API level for this device.
+int get_vendor_api_level();
 
 // Indicate whether the test is running on a GSI image.
 bool is_gsi_image();
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index 416e6c0..743928e 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -42,6 +42,8 @@
 #include <keymint_support/key_param_output.h>
 #include <keymint_support/openssl_utils.h>
 
+#include <vendorsupport/api_level.h>
+
 #include "KeyMintAidlTestBase.h"
 
 using aidl::android::hardware::security::keymint::AuthorizationSet;
@@ -4156,13 +4158,15 @@
  * when the EC_CURVE is not explicitly specified.
  */
 TEST_P(ImportKeyTest, EcdsaSuccessCurveNotSpecified) {
-    if (get_vsr_api_level() < __ANDROID_API_V__) {
+    int vendor_api_level = get_vendor_api_level();
+    if (vendor_api_level < AVendorSupport_getVendorApiLevelOf(__ANDROID_API_V__)) {
         /*
          * The KeyMint spec was previously not clear as to whether EC_CURVE was optional on import
-         * of EC keys. However, this was not checked at the time so we can only be strict about
-         * checking this for implementations at VSR-V or later.
+         * of EC keys. However, this was not checked at the time, so we version-gate the strict
+         * check.
          */
-        GTEST_SKIP() << "Skipping EC_CURVE on import only strict >= VSR-V";
+        GTEST_SKIP() << "Applies only to vendor API level >= 202404, but this device is: "
+                     << vendor_api_level;
     }
 
     ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
@@ -5314,15 +5318,15 @@
         "8564");
 
 TEST_P(ImportWrappedKeyTest, RsaKey) {
-    int vsr_api_level = get_vsr_api_level();
-    if (vsr_api_level < __ANDROID_API_V__) {
+    int vendor_api_level = get_vendor_api_level();
+    if (vendor_api_level < AVendorSupport_getVendorApiLevelOf(__ANDROID_API_V__)) {
         /*
          * The Keymaster v4 spec introduced `importWrappedKey()` and did not restrict it to
          * just symmetric keys.  However, the import of asymmetric wrapped keys was not tested
-         * at the time, so we can only be strict about checking this for implementations claiming
-         * support for VSR API level 35 and above.
+         * at the time, so we version-gate the strict check.
          */
-        GTEST_SKIP() << "Applies only to VSR API level 35, this device is: " << vsr_api_level;
+        GTEST_SKIP() << "Applies only to vendor API level >= 202404, but this device is: "
+                     << vendor_api_level;
     }
 
     auto wrapping_key_desc = AuthorizationSetBuilder()
@@ -5345,15 +5349,15 @@
 }
 
 TEST_P(ImportWrappedKeyTest, EcKey) {
-    int vsr_api_level = get_vsr_api_level();
-    if (vsr_api_level < __ANDROID_API_V__) {
+    int vendor_api_level = get_vendor_api_level();
+    if (vendor_api_level < AVendorSupport_getVendorApiLevelOf(__ANDROID_API_V__)) {
         /*
          * The Keymaster v4 spec introduced `importWrappedKey()` and did not restrict it to
          * just symmetric keys.  However, the import of asymmetric wrapped keys was not tested
-         * at the time, so we can only be strict about checking this for implementations claiming
-         * support for VSR API level 35 and above.
+         * at the time, so we version-gate the strict check.
          */
-        GTEST_SKIP() << "Applies only to VSR API level 35, this device is: " << vsr_api_level;
+        GTEST_SKIP() << "Applies only to vendor API level >= 202404, but this device is: "
+                     << vendor_api_level;
     }
 
     auto wrapping_key_desc = AuthorizationSetBuilder()
@@ -8943,27 +8947,30 @@
 
 // @VsrTest = VSR-3.10-008
 TEST_P(VsrRequirementTest, Vsr13Test) {
-    int vsr_api_level = get_vsr_api_level();
-    if (vsr_api_level < __ANDROID_API_T__) {
-        GTEST_SKIP() << "Applies only to VSR API level 33, this device is: " << vsr_api_level;
+    int vendor_api_level = get_vendor_api_level();
+    if (vendor_api_level < __ANDROID_API_T__) {
+        GTEST_SKIP() << "Applies only to vendor API level >= 33, but this device is: "
+                     << vendor_api_level;
     }
     EXPECT_GE(AidlVersion(), 2) << "VSR 13+ requires KeyMint version 2";
 }
 
 // @VsrTest = VSR-3.10-013.001
 TEST_P(VsrRequirementTest, Vsr14Test) {
-    int vsr_api_level = get_vsr_api_level();
-    if (vsr_api_level < __ANDROID_API_U__) {
-        GTEST_SKIP() << "Applies only to VSR API level 34, this device is: " << vsr_api_level;
+    int vendor_api_level = get_vendor_api_level();
+    if (vendor_api_level < __ANDROID_API_U__) {
+        GTEST_SKIP() << "Applies only to vendor API level >= 34, but this device is: "
+                     << vendor_api_level;
     }
     EXPECT_GE(AidlVersion(), 3) << "VSR 14+ requires KeyMint version 3";
 }
 
 // @VsrTest = GMS-VSR-3.10-019
 TEST_P(VsrRequirementTest, Vsr16Test) {
-    int vsr_api_level = get_vsr_api_level();
-    if (vsr_api_level <= __ANDROID_API_V__) {
-        GTEST_SKIP() << "Applies only to VSR API level > 35, this device is: " << vsr_api_level;
+    int vendor_api_level = get_vendor_api_level();
+    if (vendor_api_level <= AVendorSupport_getVendorApiLevelOf(__ANDROID_API_V__)) {
+        GTEST_SKIP() << "Applies only to vendor API level > 202404, but this device is: "
+                     << vendor_api_level;
     }
     if (SecLevel() == SecurityLevel::STRONGBOX) {
         GTEST_SKIP() << "Applies only to TEE KeyMint, not StrongBox KeyMint";
diff --git a/security/keymint/aidl/vts/functional/SecureElementProvisioningTest.cpp b/security/keymint/aidl/vts/functional/SecureElementProvisioningTest.cpp
index f7639bf..5888644 100644
--- a/security/keymint/aidl/vts/functional/SecureElementProvisioningTest.cpp
+++ b/security/keymint/aidl/vts/functional/SecureElementProvisioningTest.cpp
@@ -26,6 +26,7 @@
 #include <cppbor_parse.h>
 #include <keymaster/cppcose/cppcose.h>
 #include <keymint_support/key_param_output.h>
+#include <vendorsupport/api_level.h>
 
 #include "KeyMintAidlTestBase.h"
 
@@ -114,13 +115,14 @@
         const auto& vbKey = rot->asArray()->get(pos++);
         ASSERT_TRUE(vbKey);
         ASSERT_TRUE(vbKey->asBstr());
-        if (get_vsr_api_level() > __ANDROID_API_V__) {
+        if (get_vendor_api_level() > AVendorSupport_getVendorApiLevelOf(__ANDROID_API_V__)) {
             // The Verified Boot key field should be exactly 32 bytes since it
             // contains the SHA-256 hash of the key on locked devices or 32 bytes
             // of zeroes on unlocked devices. This wasn't checked for earlier
-            // versions of the KeyMint HAL, so only only be strict for VSR-16+.
+            // versions of the KeyMint HAL, so we version-gate the strict check.
             ASSERT_EQ(vbKey->asBstr()->value().size(), 32);
-        } else if (get_vsr_api_level() == __ANDROID_API_V__) {
+        } else if (get_vendor_api_level() ==
+                   AVendorSupport_getVendorApiLevelOf(__ANDROID_API_V__)) {
             // The Verified Boot key field should be:
             //   - Exactly 32 bytes on locked devices since it should contain
             //     the SHA-256 hash of the key, or
@@ -129,7 +131,7 @@
             //     specification).
             // Thus, we can't check for strict equality in case unlocked devices
             // report values with less than 32 bytes. This wasn't checked for
-            // earlier versions of the KeyMint HAL, so only check on VSR-15.
+            // earlier versions of the KeyMint HAL, so we version-gate the check.
             ASSERT_LE(vbKey->asBstr()->value().size(), 32);
         }
 
diff --git a/security/keymint/support/include/remote_prov/remote_prov_utils.h b/security/keymint/support/include/remote_prov/remote_prov_utils.h
index 3fa23cc..4e0ce29 100644
--- a/security/keymint/support/include/remote_prov/remote_prov_utils.h
+++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h
@@ -222,6 +222,15 @@
                                                  const std::vector<uint8_t>& csr2,
                                                  std::string_view instanceName2);
 
+/** Checks whether the component name in the configuration descriptor in the leaf certificate
+ * of the primary KeyMint instance's DICE certificate chain contains "keymint"
+ */
+ErrMsgOr<bool> verifyComponentNameInKeyMintDiceChain(const std::vector<uint8_t>& csr);
+
+/** Checks whether the DICE chain in the CSR has a certificate with a non-normal mode. */
+ErrMsgOr<bool> hasNonNormalModeInDiceChain(const std::vector<uint8_t>& csr,
+                                           std::string_view instanceName);
+
 /** Verify the DICE chain. */
 ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc,
                                                 hwtrust::DiceChain::Kind kind, bool allowAnyMode,
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
index 15b2144..5afaf31 100644
--- a/security/keymint/support/remote_prov_utils.cpp
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -45,6 +45,7 @@
 constexpr int32_t kBccPayloadKeyUsage = -4670553;
 constexpr int kP256AffinePointSize = 32;
 constexpr uint32_t kNumTeeDeviceInfoEntries = 14;
+constexpr std::string_view kKeyMintComponentName = "keymint";
 
 using EC_KEY_Ptr = bssl::UniquePtr<EC_KEY>;
 using EVP_PKEY_Ptr = bssl::UniquePtr<EVP_PKEY>;
@@ -869,7 +870,7 @@
     }
 
     auto csr = hwtrust::Csr::validate(encodedCsr, *diceChainKind, false /*isFactory*/,
-                                      false /*allowAnyMode*/, deviceSuffix(instanceName));
+                                      true /*allowAnyMode*/, deviceSuffix(instanceName));
     if (!csr.ok()) {
         return csr.error().message();
     }
@@ -882,7 +883,7 @@
     return diceChain->IsProper();
 }
 
-std::string hexlify(const std::vector<unsigned char>& bytes) {
+std::string hexlify(const std::vector<uint8_t>& bytes) {
     std::stringstream ss;
     ss << std::hex << std::setfill('0');
 
@@ -903,7 +904,7 @@
     }
 
     auto csr1 = hwtrust::Csr::validate(encodedCsr1, *diceChainKind, false /*isFactory*/,
-                                       false /*allowAnyMode*/, deviceSuffix(instanceName1));
+                                       true /*allowAnyMode*/, deviceSuffix(instanceName1));
     if (!csr1.ok()) {
         return csr1.error().message();
     }
@@ -920,7 +921,7 @@
     }
 
     auto csr2 = hwtrust::Csr::validate(encodedCsr2, *diceChainKind, false /*isFactory*/,
-                                       false /*allowAnyMode*/, deviceSuffix(instanceName2));
+                                       true /*allowAnyMode*/, deviceSuffix(instanceName2));
     if (!csr2.ok()) {
         return csr2.error().message();
     }
@@ -944,4 +945,55 @@
     return *result;
 }
 
+ErrMsgOr<bool> verifyComponentNameInKeyMintDiceChain(const std::vector<uint8_t>& encodedCsr) {
+    auto diceChainKind = getDiceChainKind();
+    if (!diceChainKind) {
+        return diceChainKind.message();
+    }
+
+    auto csr = hwtrust::Csr::validate(encodedCsr, *diceChainKind, false /*isFactory*/,
+                                      true /*allowAnyMode*/, deviceSuffix(DEFAULT_INSTANCE_NAME));
+    if (!csr.ok()) {
+        return csr.error().message();
+    }
+
+    auto diceChain = csr->getDiceChain();
+    if (!diceChain.ok()) {
+        return diceChain.error().message();
+    }
+
+    auto satisfied = diceChain->componentNameContains(kKeyMintComponentName);
+    if (!satisfied.ok()) {
+        return satisfied.error().message();
+    }
+
+    return *satisfied;
+}
+
+ErrMsgOr<bool> hasNonNormalModeInDiceChain(const std::vector<uint8_t>& encodedCsr,
+                                           std::string_view instanceName) {
+    auto diceChainKind = getDiceChainKind();
+    if (!diceChainKind) {
+        return diceChainKind.message();
+    }
+
+    auto csr = hwtrust::Csr::validate(encodedCsr, *diceChainKind, false /*isFactory*/,
+                                      true /*allowAnyMode*/, deviceSuffix(instanceName));
+    if (!csr.ok()) {
+        return csr.error().message();
+    }
+
+    auto diceChain = csr->getDiceChain();
+    if (!diceChain.ok()) {
+        return diceChain.error().message();
+    }
+
+    auto hasNonNormalModeInDiceChain = diceChain->hasNonNormalMode();
+    if (!hasNonNormalModeInDiceChain.ok()) {
+        return hasNonNormalModeInDiceChain.error().message();
+    }
+
+    return *hasNonNormalModeInDiceChain;
+}
+
 }  // namespace aidl::android::hardware::security::keymint::remote_prov
diff --git a/security/keymint/support/remote_prov_utils_test.cpp b/security/keymint/support/remote_prov_utils_test.cpp
index c6080cd..e3c1e58 100644
--- a/security/keymint/support/remote_prov_utils_test.cpp
+++ b/security/keymint/support/remote_prov_utils_test.cpp
@@ -99,7 +99,7 @@
         0x02, 0xb4, 0x8a, 0xd2, 0x4c, 0xc4, 0x70, 0x6b, 0x88, 0x98, 0x23, 0x9e, 0xb3, 0x52, 0xb1};
 
 inline const std::vector<uint8_t> kCsrWithDegenerateDiceChain{
-        0x85, 0x01, 0xa0, 0x82, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0xf2,
+        0x84, 0x01, 0xa0, 0x82, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0xf2,
         0xc6, 0x50, 0xd2, 0x42, 0x59, 0xe0, 0x4e, 0x7b, 0xc0, 0x75, 0x41, 0xa2, 0xe9, 0xd0, 0xe8,
         0x18, 0xd7, 0xd7, 0x63, 0x7e, 0x41, 0x04, 0x7e, 0x52, 0x1a, 0xb1, 0xb7, 0xdc, 0x13, 0xb3,
         0x0f, 0x22, 0x58, 0x20, 0x1a, 0xf3, 0x8b, 0x0f, 0x7a, 0xc6, 0xf2, 0xb8, 0x31, 0x0b, 0x40,
@@ -157,12 +157,7 @@
         0xe2, 0xfe, 0x7c, 0x0d, 0x2c, 0x88, 0x3b, 0x23, 0x66, 0x93, 0x7b, 0x94, 0x59, 0xc4, 0x87,
         0x16, 0xc4, 0x3a, 0x85, 0x60, 0xe3, 0x62, 0x45, 0x53, 0xa8, 0x1d, 0x4e, 0xa4, 0x2b, 0x61,
         0x33, 0x17, 0x71, 0xb6, 0x40, 0x11, 0x7d, 0x23, 0x64, 0xe6, 0x49, 0xbe, 0xa6, 0x85, 0x32,
-        0x1a, 0x89, 0xa1, 0x6b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74,
-        0x78, 0x3b, 0x62, 0x72, 0x61, 0x6e, 0x64, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63,
-        0x74, 0x31, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x31, 0x3a, 0x31, 0x31, 0x2f, 0x69,
-        0x64, 0x2f, 0x32, 0x30, 0x32, 0x31, 0x30, 0x38, 0x30, 0x35, 0x2e, 0x34, 0x32, 0x3a, 0x75,
-        0x73, 0x65, 0x72, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2d, 0x6b, 0x65, 0x79,
-        0x73};
+        0x1a, 0x89};
 
 // The challenge that is in kKeysToSignForCsrWithUdsCerts and kCsrWithUdsCerts
 inline const std::vector<uint8_t> kChallenge{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
@@ -189,7 +184,7 @@
         0xc9, 0x0a};
 
 inline const std::vector<uint8_t> kCsrWithUdsCerts{
-        0x85, 0x01, 0xa1, 0x70, 0x74, 0x65, 0x73, 0x74, 0x2d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72,
+        0x84, 0x01, 0xa1, 0x70, 0x74, 0x65, 0x73, 0x74, 0x2d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72,
         0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0x59, 0x01, 0x6c, 0x30, 0x82, 0x01, 0x68, 0x30, 0x82,
         0x01, 0x1a, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x7b, 0x30, 0x05, 0x06, 0x03, 0x2b,
         0x65, 0x70, 0x30, 0x2b, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
@@ -310,12 +305,7 @@
         0x48, 0x3c, 0xab, 0xd3, 0x74, 0xf8, 0x41, 0x88, 0x9b, 0x48, 0xf3, 0x93, 0x06, 0x40, 0x1b,
         0x5f, 0x60, 0x7b, 0xbe, 0xd8, 0xa6, 0x65, 0xff, 0x6a, 0x89, 0x24, 0x12, 0x1b, 0xac, 0xa3,
         0xd5, 0x37, 0x85, 0x6e, 0x53, 0x8d, 0xa5, 0x07, 0xe7, 0xe7, 0x44, 0x2c, 0xba, 0xa0, 0xbe,
-        0x1a, 0x43, 0xde, 0x28, 0x59, 0x65, 0xa1, 0x6b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70,
-        0x72, 0x69, 0x6e, 0x74, 0x78, 0x3b, 0x62, 0x72, 0x61, 0x6e, 0x64, 0x31, 0x2f, 0x70, 0x72,
-        0x6f, 0x64, 0x75, 0x63, 0x74, 0x31, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x31, 0x3a,
-        0x31, 0x31, 0x2f, 0x69, 0x64, 0x2f, 0x32, 0x30, 0x32, 0x31, 0x30, 0x38, 0x30, 0x35, 0x2e,
-        0x34, 0x32, 0x3a, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65,
-        0x2d, 0x6b, 0x65, 0x79, 0x73};
+        0x1a, 0x43, 0xde, 0x28, 0x59, 0x65};
 
 inline const std::vector<uint8_t> kKeysToSignForCsrWithoutUdsCerts = {
         0x82, 0xa6, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x11, 0x29, 0x11, 0x96,
@@ -408,8 +398,149 @@
         0x35, 0x2e, 0x34, 0x32, 0x3a, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61,
         0x73, 0x65, 0x2d, 0x6b, 0x65, 0x79, 0x73};
 
+inline const std::vector<uint8_t> kCsrWithKeyMintInComponentName{
+        0x84, 0x01, 0xa0, 0x82, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x44,
+        0xfd, 0xdd, 0xf1, 0x8a, 0x78, 0xa0, 0xbe, 0x37, 0x49, 0x51, 0x85, 0xfb, 0x7a, 0x16, 0xca,
+        0xc1, 0x00, 0xb3, 0x78, 0x13, 0x4a, 0x90, 0x4c, 0x5a, 0xa1, 0x3b, 0xfc, 0xea, 0xb6, 0xf3,
+        0x16, 0x22, 0x58, 0x20, 0x12, 0x7f, 0xf5, 0xe2, 0x14, 0xbd, 0x5d, 0x51, 0xd3, 0x7f, 0x2f,
+        0x1f, 0x9c, 0xc2, 0x18, 0x31, 0x94, 0x13, 0x52, 0x31, 0x3d, 0x43, 0xc9, 0x39, 0xbc, 0xc9,
+        0x06, 0xb0, 0xb2, 0xa9, 0x0e, 0x59, 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa0, 0x59, 0x01, 0x0e,
+        0xa9, 0x01, 0x66, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x02, 0x67, 0x73, 0x75, 0x62, 0x6a,
+        0x65, 0x63, 0x74, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x20, 0x55, 0x55, 0x55, 0x55, 0x55,
+        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x3a, 0x00, 0x47,
+        0x44, 0x52, 0x58, 0x20, 0x88, 0x44, 0x82, 0xb6, 0x6c, 0x9c, 0xcb, 0x2e, 0xe8, 0xdc, 0xb4,
+        0xe9, 0xd3, 0xf7, 0x87, 0x63, 0x03, 0xaa, 0x90, 0x9d, 0xd9, 0xcb, 0xc3, 0x81, 0x03, 0x5b,
+        0xa9, 0xa2, 0x75, 0xe8, 0xfa, 0x50, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x58, 0x1e, 0xa1, 0x3a,
+        0x00, 0x01, 0x11, 0x71, 0x77, 0x4d, 0x6f, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20,
+        0x6a, 0x75, 0x73, 0x74, 0x20, 0x6b, 0x65, 0x79, 0x6d, 0x69, 0x6e, 0x74, 0x3f, 0x3a, 0x00,
+        0x47, 0x44, 0x54, 0x58, 0x20, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x3a, 0x00, 0x47, 0x44, 0x56, 0x41, 0x01, 0x3a,
+        0x00, 0x47, 0x44, 0x57, 0x58, 0x4d, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58,
+        0x20, 0xe7, 0x0e, 0x4b, 0xcb, 0x8d, 0x4a, 0xd0, 0x36, 0x7f, 0xbf, 0x19, 0xdd, 0x9a, 0xd9,
+        0x43, 0x62, 0xab, 0x29, 0x13, 0x1e, 0x14, 0x98, 0x68, 0xee, 0x5d, 0xae, 0xfa, 0x2f, 0x49,
+        0x99, 0xfa, 0x0b, 0x22, 0x58, 0x20, 0xfb, 0x9f, 0xd3, 0xd8, 0x67, 0x86, 0x81, 0x5f, 0xb3,
+        0x84, 0x00, 0x32, 0xc4, 0xe8, 0x7e, 0x9b, 0xb2, 0x4c, 0x1d, 0xd7, 0x71, 0x0d, 0x8f, 0xe0,
+        0x26, 0xc6, 0x73, 0xb1, 0x8f, 0x2d, 0x02, 0xf8, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20,
+        0x58, 0x40, 0x8f, 0xb9, 0x41, 0x5f, 0xa7, 0x48, 0xe4, 0xe3, 0xfb, 0x80, 0xbf, 0x2e, 0x30,
+        0xb8, 0xc9, 0x2c, 0xe4, 0x74, 0x4f, 0x4c, 0xb1, 0x79, 0xfb, 0x42, 0x43, 0x1e, 0xb4, 0x6c,
+        0x6a, 0x37, 0xaf, 0x3a, 0x76, 0x15, 0x16, 0x1f, 0x25, 0x26, 0x27, 0x1b, 0x9c, 0x5c, 0xcf,
+        0x9a, 0xfd, 0xb0, 0x82, 0xad, 0xe4, 0xd1, 0xd9, 0x04, 0x80, 0x80, 0x1a, 0x21, 0x12, 0x3e,
+        0x81, 0xff, 0x0c, 0x23, 0x3d, 0x0a, 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa0, 0x59, 0x02, 0x10,
+        0x82, 0x58, 0x20, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+        0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+        0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x59, 0x01, 0xea, 0x84, 0x03, 0x67, 0x6b, 0x65, 0x79, 0x6d,
+        0x69, 0x6e, 0x74, 0xae, 0x65, 0x62, 0x72, 0x61, 0x6e, 0x64, 0x66, 0x47, 0x6f, 0x6f, 0x67,
+        0x6c, 0x65, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64, 0x01, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x6c,
+        0x65, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x66, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x66, 0x64,
+        0x65, 0x76, 0x69, 0x63, 0x65, 0x67, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x65, 0x70,
+        0x69, 0x78, 0x65, 0x6c, 0x68, 0x76, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x65, 0x67,
+        0x72, 0x65, 0x65, 0x6e, 0x6a, 0x6f, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+        0x62, 0x31, 0x32, 0x6c, 0x6d, 0x61, 0x6e, 0x75, 0x66, 0x61, 0x63, 0x74, 0x75, 0x72, 0x65,
+        0x72, 0x66, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x6d, 0x76, 0x62, 0x6d, 0x65, 0x74, 0x61,
+        0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x4f, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+        0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69,
+        0x74, 0x79, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x63, 0x74, 0x65, 0x65, 0x70, 0x62, 0x6f,
+        0x6f, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x1a,
+        0x01, 0x34, 0x8c, 0x62, 0x70, 0x62, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72,
+        0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x66, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x72, 0x73,
+        0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6c, 0x65, 0x76,
+        0x65, 0x6c, 0x1a, 0x01, 0x34, 0x8c, 0x61, 0x72, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f,
+        0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x1a, 0x01, 0x34, 0x8c,
+        0x63, 0x82, 0xa6, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x40, 0x9a, 0x39,
+        0x4d, 0xe2, 0xc5, 0x48, 0x58, 0x6e, 0xaa, 0x17, 0x81, 0x8a, 0xba, 0xc4, 0xea, 0x20, 0x23,
+        0xc4, 0xf3, 0xdc, 0xfa, 0x78, 0x9f, 0x5b, 0x7d, 0x30, 0xd2, 0x3d, 0x6f, 0x42, 0xa8, 0x22,
+        0x58, 0x20, 0x3e, 0x85, 0x73, 0xe5, 0x86, 0x57, 0x45, 0x72, 0xcd, 0xf2, 0xe8, 0x62, 0x93,
+        0xf3, 0xea, 0xbf, 0x21, 0x57, 0xa9, 0x25, 0xe7, 0xa8, 0x2a, 0xa0, 0xd0, 0x0a, 0x1d, 0xa5,
+        0x81, 0x22, 0xd0, 0xe1, 0x23, 0x58, 0x21, 0x00, 0x95, 0x00, 0xea, 0x43, 0x32, 0x8d, 0x2e,
+        0x1c, 0x18, 0x6d, 0x73, 0x34, 0xa2, 0xe4, 0x53, 0x64, 0x20, 0x95, 0x25, 0x8d, 0x4b, 0x97,
+        0x71, 0x13, 0xdf, 0xa6, 0x0e, 0xdc, 0x5b, 0x66, 0x66, 0x0b, 0xa6, 0x01, 0x02, 0x03, 0x26,
+        0x20, 0x01, 0x21, 0x58, 0x20, 0xa4, 0x81, 0xd9, 0xa0, 0x37, 0x89, 0xb1, 0x5d, 0x22, 0xf1,
+        0x22, 0x7e, 0x19, 0x00, 0xf1, 0x87, 0xdf, 0x10, 0x88, 0x1f, 0x64, 0xc5, 0x46, 0x6d, 0x91,
+        0x9d, 0x6c, 0x86, 0x3b, 0x67, 0x07, 0x14, 0x22, 0x58, 0x20, 0x3c, 0x42, 0x1f, 0x47, 0x62,
+        0x2c, 0x4f, 0x08, 0x29, 0xe9, 0xc6, 0x0c, 0x7c, 0xe5, 0x11, 0xc6, 0x39, 0x69, 0xc9, 0x9d,
+        0xdf, 0x69, 0x18, 0x8e, 0xdb, 0x99, 0x87, 0x52, 0x2f, 0x6b, 0xb4, 0x6a, 0x23, 0x58, 0x21,
+        0x00, 0x83, 0xa2, 0x6f, 0x5b, 0x3a, 0x71, 0x7c, 0x66, 0x3c, 0x96, 0x3f, 0x4e, 0x42, 0x6f,
+        0x98, 0x45, 0x1f, 0x59, 0x50, 0x03, 0x48, 0x9b, 0x57, 0xf5, 0x0e, 0x0f, 0x96, 0x30, 0xa4,
+        0xd6, 0xc3, 0x45, 0x58, 0x40, 0x8b, 0xdf, 0xc8, 0xc7, 0x6d, 0xa4, 0x02, 0xec, 0x3c, 0x5d,
+        0x90, 0x73, 0x0f, 0x8c, 0x10, 0x0f, 0x99, 0xd2, 0x85, 0x6e, 0x03, 0x45, 0x55, 0x28, 0xf7,
+        0x64, 0x0b, 0xbd, 0x7c, 0x3a, 0x69, 0xf1, 0x80, 0x1a, 0xf3, 0x93, 0x7e, 0x82, 0xfc, 0xa5,
+        0x3b, 0x69, 0x98, 0xf1, 0xde, 0x06, 0xb6, 0x72, 0x78, 0x0b, 0xdb, 0xbb, 0x97, 0x20, 0x04,
+        0x98, 0xb0, 0xd4, 0x07, 0x83, 0x65, 0xfb, 0xf8, 0x9c};
+
+inline std::vector<uint8_t> kCsrWithDebugMode{
+        0x84, 0x01, 0xa0, 0x82, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x03,
+        0x09, 0xad, 0x0d, 0x07, 0xec, 0x59, 0xfc, 0x14, 0x31, 0x21, 0x1f, 0xbc, 0x8e, 0x44, 0xe7,
+        0x0f, 0xa9, 0xb7, 0x5a, 0x57, 0x38, 0x5f, 0x76, 0x8a, 0xa3, 0x38, 0x2c, 0xf0, 0x1b, 0x37,
+        0x15, 0x22, 0x58, 0x20, 0x82, 0xae, 0x09, 0x76, 0x9c, 0x1d, 0x18, 0x39, 0x5d, 0x09, 0xf8,
+        0x19, 0x86, 0x70, 0x60, 0x12, 0x1e, 0x06, 0xb3, 0x68, 0x4a, 0x27, 0x2c, 0x79, 0xd3, 0x83,
+        0xb8, 0x54, 0x5b, 0x9f, 0xc0, 0x9f, 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa0, 0x59, 0x01, 0x04,
+        0xa9, 0x01, 0x66, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x02, 0x67, 0x73, 0x75, 0x62, 0x6a,
+        0x65, 0x63, 0x74, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x20, 0x55, 0x55, 0x55, 0x55, 0x55,
+        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x3a, 0x00, 0x47,
+        0x44, 0x52, 0x58, 0x20, 0xb8, 0x96, 0x54, 0xe2, 0x2c, 0xa4, 0xd2, 0x4a, 0x9c, 0x0e, 0x45,
+        0x11, 0xc8, 0xf2, 0x63, 0xf0, 0x66, 0x0d, 0x2e, 0x20, 0x48, 0x96, 0x90, 0x14, 0xf4, 0x54,
+        0x63, 0xc4, 0xf4, 0x39, 0x30, 0x38, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x55, 0xa1, 0x3a, 0x00,
+        0x01, 0x11, 0x71, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x6e,
+        0x61, 0x6d, 0x65, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x20, 0x55, 0x55, 0x55, 0x55, 0x55,
+        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x3a, 0x00, 0x47,
+        0x44, 0x56, 0x41, 0x02, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x4d, 0xa5, 0x01, 0x02, 0x03,
+        0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x5f, 0xcf, 0x63, 0xcc, 0x24, 0x16, 0x66, 0x11, 0x6c,
+        0xaf, 0xed, 0xf4, 0x02, 0x8f, 0xc9, 0x14, 0xc7, 0x32, 0xa5, 0xdb, 0x41, 0x53, 0x54, 0x34,
+        0xcf, 0xcd, 0x6b, 0x4c, 0xb2, 0x22, 0x89, 0x96, 0x22, 0x58, 0x20, 0x4c, 0xed, 0xe3, 0x92,
+        0x94, 0x8f, 0x04, 0xd6, 0x04, 0x3a, 0x6c, 0x15, 0x5f, 0xbb, 0x52, 0x4a, 0x7d, 0x94, 0xcf,
+        0x31, 0x49, 0x31, 0x28, 0x66, 0x38, 0xe2, 0x40, 0x3e, 0xd0, 0xc1, 0x2b, 0xf4, 0x3a, 0x00,
+        0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40, 0xb6, 0x0b, 0xa7, 0x9c, 0x02, 0x6d, 0x64, 0x98,
+        0x9d, 0x73, 0x1b, 0x74, 0x21, 0x32, 0xf6, 0xb6, 0x1e, 0x6d, 0x07, 0x56, 0x0f, 0x5f, 0x14,
+        0x60, 0xf9, 0x68, 0xb5, 0xee, 0xa5, 0xf9, 0x9b, 0xa9, 0x88, 0x4c, 0x55, 0x75, 0x26, 0xa7,
+        0x84, 0xee, 0x07, 0x8a, 0xdc, 0xeb, 0x6d, 0xfe, 0x53, 0xe4, 0x38, 0xa8, 0x11, 0x1f, 0x57,
+        0xbe, 0x77, 0x51, 0xc7, 0xbe, 0x4b, 0xd5, 0x2f, 0x9c, 0x2a, 0xcd, 0x84, 0x43, 0xa1, 0x01,
+        0x26, 0xa0, 0x59, 0x02, 0x0f, 0x82, 0x58, 0x20, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+        0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x59, 0x01, 0xe9, 0x84, 0x03,
+        0x67, 0x6b, 0x65, 0x79, 0x6d, 0x69, 0x6e, 0x74, 0xae, 0x65, 0x62, 0x72, 0x61, 0x6e, 0x64,
+        0x66, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64, 0x01, 0x65,
+        0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x66, 0x64, 0x65, 0x76,
+        0x69, 0x63, 0x65, 0x66, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x67, 0x70, 0x72, 0x6f, 0x64,
+        0x75, 0x63, 0x74, 0x65, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x68, 0x76, 0x62, 0x5f, 0x73, 0x74,
+        0x61, 0x74, 0x65, 0x65, 0x67, 0x72, 0x65, 0x65, 0x6e, 0x6a, 0x6f, 0x73, 0x5f, 0x76, 0x65,
+        0x72, 0x73, 0x69, 0x6f, 0x6e, 0x62, 0x31, 0x32, 0x6c, 0x6d, 0x61, 0x6e, 0x75, 0x66, 0x61,
+        0x63, 0x74, 0x75, 0x72, 0x65, 0x72, 0x66, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x6d, 0x76,
+        0x62, 0x6d, 0x65, 0x74, 0x61, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x4f, 0x11, 0x22,
+        0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x6e, 0x73,
+        0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x63, 0x74,
+        0x65, 0x65, 0x70, 0x62, 0x6f, 0x6f, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6c,
+        0x65, 0x76, 0x65, 0x6c, 0x1a, 0x01, 0x34, 0x8c, 0x62, 0x70, 0x62, 0x6f, 0x6f, 0x74, 0x6c,
+        0x6f, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x66, 0x6c, 0x6f, 0x63,
+        0x6b, 0x65, 0x64, 0x72, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x70, 0x61, 0x74, 0x63,
+        0x68, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x1a, 0x01, 0x34, 0x8c, 0x61, 0x72, 0x76, 0x65,
+        0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6c, 0x65, 0x76, 0x65,
+        0x6c, 0x1a, 0x01, 0x34, 0x8c, 0x63, 0x82, 0xa6, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21,
+        0x58, 0x20, 0x78, 0xa9, 0x66, 0xc0, 0xc1, 0x92, 0xc9, 0x0d, 0x74, 0xbf, 0x81, 0x99, 0xe6,
+        0x7c, 0x61, 0xb2, 0xc3, 0x41, 0x27, 0x4e, 0x92, 0xd8, 0xd8, 0xf8, 0x34, 0x43, 0x81, 0xe1,
+        0x9c, 0x5f, 0xed, 0xbb, 0x22, 0x58, 0x20, 0x80, 0x05, 0x96, 0xe5, 0x65, 0x7e, 0xa8, 0x5b,
+        0x58, 0xf7, 0x2c, 0xb8, 0x28, 0x95, 0x34, 0x7a, 0x88, 0xd5, 0xe2, 0x1c, 0x20, 0x01, 0xd7,
+        0x60, 0xfe, 0xec, 0xf1, 0x3f, 0x69, 0xfc, 0xc0, 0xa4, 0x23, 0x58, 0x21, 0x00, 0x99, 0xad,
+        0x34, 0x13, 0xf0, 0x7f, 0xaa, 0xf7, 0xda, 0xe9, 0x95, 0xfe, 0x2a, 0x36, 0xf5, 0xac, 0xbb,
+        0xad, 0x39, 0x8f, 0x3f, 0x86, 0x10, 0x39, 0x2c, 0xa9, 0x4c, 0xb9, 0xbb, 0x79, 0x10, 0x45,
+        0xa6, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0xd2, 0x94, 0xcb, 0x74, 0x5f,
+        0x9c, 0xc2, 0x1e, 0x47, 0x28, 0x69, 0x85, 0x6e, 0xf2, 0x34, 0x0a, 0x62, 0x90, 0xc3, 0xb0,
+        0xc9, 0x3a, 0xb2, 0x32, 0xdc, 0x99, 0x19, 0x9b, 0x36, 0xde, 0x78, 0xed, 0x22, 0x58, 0x20,
+        0xd9, 0x81, 0x04, 0x83, 0xbb, 0x85, 0x3e, 0x3b, 0x46, 0xe8, 0xe1, 0xa3, 0x8a, 0x04, 0xb9,
+        0x3f, 0x74, 0x4e, 0x5c, 0x96, 0x21, 0x5c, 0x79, 0x0b, 0x8e, 0x4e, 0x7d, 0x61, 0x1b, 0x69,
+        0xb2, 0x46, 0x23, 0x58, 0x20, 0x2f, 0x79, 0xf7, 0xbb, 0xbb, 0x7e, 0xee, 0x15, 0x61, 0xa2,
+        0x78, 0x5f, 0x9c, 0x8c, 0xaf, 0x52, 0xcb, 0xbe, 0x24, 0x31, 0xa8, 0x95, 0x86, 0x8d, 0xed,
+        0x98, 0x80, 0x71, 0x53, 0x91, 0xb3, 0x87, 0x58, 0x40, 0x19, 0xc9, 0xc4, 0x4e, 0x8b, 0xae,
+        0x26, 0x7f, 0xdd, 0x9c, 0xac, 0xe2, 0xbf, 0xe2, 0xfb, 0x3c, 0x3f, 0xd6, 0x6f, 0x9a, 0x97,
+        0xc3, 0x2a, 0x60, 0xfe, 0x0e, 0x9f, 0x11, 0xc9, 0x04, 0xa7, 0xdf, 0xe1, 0x21, 0x1e, 0xc1,
+        0x10, 0x10, 0x64, 0xf7, 0xeb, 0xcc, 0x3a, 0x4c, 0xa6, 0xdf, 0xd8, 0xf5, 0xcc, 0x0d, 0x34,
+        0xa4, 0x32, 0xf4, 0x0a, 0xd7, 0x83, 0x1e, 0x30, 0x0d, 0x68, 0x6a, 0xb4, 0xc1};
+
 inline const std::vector<uint8_t> kCsrWithSharedUdsRoot1{
-        0x85, 0x01, 0xa0, 0x82, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x96,
+        0x84, 0x01, 0xa0, 0x82, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x96,
         0xf9, 0xf7, 0x16, 0xa7, 0xe2, 0x20, 0xe3, 0x6e, 0x19, 0x8e, 0xc0, 0xc4, 0x82, 0xc5, 0xca,
         0x8d, 0x1d, 0xb4, 0xda, 0x94, 0x6d, 0xf8, 0xbc, 0x0b, 0x0e, 0xc7, 0x90, 0x83, 0x5b, 0xc3,
         0x4b, 0x22, 0x58, 0x20, 0xed, 0xe0, 0xa1, 0x56, 0x46, 0x5b, 0xe0, 0x67, 0x2d, 0xbc, 0x08,
@@ -476,15 +607,10 @@
         0x08, 0x8a, 0x5b, 0xb9, 0xef, 0x28, 0x5a, 0xe0, 0x02, 0x40, 0xf5, 0x68, 0x49, 0x8b, 0xa7,
         0xf7, 0x9d, 0xa3, 0xb3, 0x37, 0x72, 0x79, 0xa9, 0x32, 0x47, 0xf6, 0x8d, 0x5d, 0x08, 0xe7,
         0xec, 0x00, 0x19, 0x09, 0x6f, 0x0a, 0x4d, 0x7c, 0x62, 0x6c, 0x2b, 0xaa, 0x33, 0x61, 0xe5,
-        0xa5, 0x3f, 0x2a, 0xfe, 0xcc, 0xdf, 0x8e, 0x62, 0x1c, 0x31, 0xe1, 0x56, 0x6b, 0xa1, 0x6b,
-        0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x78, 0x3b, 0x62, 0x72,
-        0x61, 0x6e, 0x64, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x31, 0x2f, 0x64,
-        0x65, 0x76, 0x69, 0x63, 0x65, 0x31, 0x3a, 0x31, 0x31, 0x2f, 0x69, 0x64, 0x2f, 0x32, 0x30,
-        0x32, 0x31, 0x30, 0x38, 0x30, 0x35, 0x2e, 0x34, 0x32, 0x3a, 0x75, 0x73, 0x65, 0x72, 0x2f,
-        0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2d, 0x6b, 0x65, 0x79, 0x73};
+        0xa5, 0x3f, 0x2a, 0xfe, 0xcc, 0xdf, 0x8e, 0x62, 0x1c, 0x31, 0xe1, 0x56, 0x6b};
 
 inline const std::vector<uint8_t> kCsrWithSharedUdsRoot2{
-        0x85, 0x01, 0xa0, 0x82, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x96,
+        0x84, 0x01, 0xa0, 0x82, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x96,
         0xf9, 0xf7, 0x16, 0xa7, 0xe2, 0x20, 0xe3, 0x6e, 0x19, 0x8e, 0xc0, 0xc4, 0x82, 0xc5, 0xca,
         0x8d, 0x1d, 0xb4, 0xda, 0x94, 0x6d, 0xf8, 0xbc, 0x0b, 0x0e, 0xc7, 0x90, 0x83, 0x5b, 0xc3,
         0x4b, 0x22, 0x58, 0x20, 0xed, 0xe0, 0xa1, 0x56, 0x46, 0x5b, 0xe0, 0x67, 0x2d, 0xbc, 0x08,
@@ -551,12 +677,7 @@
         0xbf, 0xd5, 0x06, 0x2c, 0xac, 0x18, 0x3c, 0xbb, 0xc6, 0x77, 0x99, 0x2f, 0x4e, 0x71, 0xcd,
         0x7a, 0x9b, 0x93, 0xc7, 0x08, 0xa3, 0x71, 0x89, 0xb5, 0xb2, 0x04, 0xbe, 0x69, 0x22, 0xf3,
         0x66, 0xb8, 0xa9, 0xc6, 0x5e, 0x7c, 0x45, 0xf6, 0x2f, 0x8a, 0xa9, 0x3e, 0xee, 0x6f, 0x92,
-        0x2a, 0x9c, 0x91, 0xe2, 0x1d, 0x4a, 0x4e, 0x4a, 0xb4, 0xcc, 0x87, 0xd2, 0x85, 0x5f, 0xa1,
-        0x6b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x78, 0x3b, 0x62,
-        0x72, 0x61, 0x6e, 0x64, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x31, 0x2f,
-        0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x31, 0x3a, 0x31, 0x31, 0x2f, 0x69, 0x64, 0x2f, 0x32,
-        0x30, 0x32, 0x31, 0x30, 0x38, 0x30, 0x35, 0x2e, 0x34, 0x32, 0x3a, 0x75, 0x73, 0x65, 0x72,
-        0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2d, 0x6b, 0x65, 0x79, 0x73};
+        0x2a, 0x9c, 0x91, 0xe2, 0x1d, 0x4a, 0x4e, 0x4a, 0xb4, 0xcc, 0x87, 0xd2, 0x85, 0x5f};
 
 const RpcHardwareInfo kRpcHardwareInfo = {.versionNumber = 3};
 
@@ -930,6 +1051,27 @@
     ASSERT_FALSE(*notEqual) << "Root public keys in DICE chains match.";
 }
 
+TEST(RemoteProvUtilsTest, componentNameInLeafCertificateOfDiceChainContainsKeyMint) {
+    auto result = verifyComponentNameInKeyMintDiceChain(kCsrWithKeyMintInComponentName);
+    ASSERT_TRUE(result) << result.message();
+    ASSERT_TRUE(*result) << "Leaf Certificate in CSR does not contain 'keymint' in component name";
+
+    auto result2 = verifyComponentNameInKeyMintDiceChain(kCsrWithUdsCerts);
+    ASSERT_TRUE(result2) << result2.message();
+    ASSERT_FALSE(*result2) << "Leaf Certificate in CSR contains 'keymint' in component name";
+}
+
+TEST(RemoteProvUtilsTest, checkModeOnCertificatesInDiceChain) {
+    auto hasNonNormalMode = hasNonNormalModeInDiceChain(kCsrWithDebugMode, DEFAULT_INSTANCE_NAME);
+    ASSERT_TRUE(hasNonNormalMode) << hasNonNormalMode.message();
+    ASSERT_TRUE(*hasNonNormalMode);
+
+    auto hasNonNormalModeInDiceChain2 =
+            hasNonNormalModeInDiceChain(kCsrWithUdsCerts, DEFAULT_INSTANCE_NAME);
+    ASSERT_TRUE(hasNonNormalModeInDiceChain2) << hasNonNormalModeInDiceChain2.message();
+    ASSERT_FALSE(*hasNonNormalModeInDiceChain2);
+}
+
 TEST(RemoteProvUtilsTest, parseFullyQualifiedInstanceNames) {
     ASSERT_EQ(deviceSuffix(RKPVM_INSTANCE_NAME), "avf");
     ASSERT_EQ(deviceSuffix(DEFAULT_INSTANCE_NAME), "default");
diff --git a/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index d9bd0d8..810cc38 100644
--- a/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -186,10 +186,10 @@
             if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
                 GTEST_SKIP() << "The RKP VM is not supported on this system.";
             }
-            int apiLevel = get_vsr_api_level();
-            if (apiLevel < __ANDROID_API_V__) {
-                GTEST_SKIP() << "The RKP VM is supported only on V+ devices. Vendor API level: "
-                             << apiLevel;
+            int vendorApiLevel = get_vendor_api_level();
+            if (vendorApiLevel < __ANDROID_API_V__) {
+                GTEST_SKIP() << "The RKP VM is supported only on vendor API level >= 202404. This "
+                             << "device has vendor API level: " << vendorApiLevel;
             }
         }
         ASSERT_TRUE(status.isOk());
@@ -238,11 +238,12 @@
  * on the device.
  */
 // @VsrTest = 3.10-015
+// @VsrTest = 3.10-018.001
 TEST(NonParameterizedTests, requireDiceOnDefaultInstanceIfStrongboxPresent) {
-    int vsr_api_level = get_vsr_api_level();
-    if (vsr_api_level < 35) {
-        GTEST_SKIP() << "Applies only to VSR API level 35 or newer, this device is: "
-                     << vsr_api_level;
+    int vendor_api_level = get_vendor_api_level();
+    if (vendor_api_level < __ANDROID_API_V__) {
+        GTEST_SKIP() << "Applies only to vendor API level >= 202404, but this device is: "
+                     << vendor_api_level;
     }
 
     if (!AServiceManager_isDeclared(KEYMINT_STRONGBOX_INSTANCE_NAME.c_str())) {
@@ -269,13 +270,11 @@
  */
 // @VsrTest = 7.1-003.001
 TEST(NonParameterizedTests, equalUdsPubInDiceCertChainForRkpVmAndPrimaryKeyMintInstances) {
-    int apiLevel = get_vsr_api_level();
-    if (apiLevel < 202504) {
-        if (!AServiceManager_isDeclared(RKPVM_INSTANCE_NAME.c_str())) {
-            GTEST_SKIP() << "The RKP VM (" << RKPVM_INSTANCE_NAME
-                         << ") is not present on this device.";
-        }
-    } else {
+    int vendorApiLevel = get_vendor_api_level();
+    if (vendorApiLevel < 202504 && !AServiceManager_isDeclared(RKPVM_INSTANCE_NAME.c_str())) {
+        GTEST_SKIP() << "The RKP VM (" << RKPVM_INSTANCE_NAME << ") is not present on this device.";
+    }
+    if (vendorApiLevel >= 202504) {
         ASSERT_TRUE(AServiceManager_isDeclared(RKPVM_INSTANCE_NAME.c_str()));
     }
 
@@ -311,6 +310,40 @@
     ASSERT_TRUE(*equal) << "Primary KeyMint and RKP VM RPCs have different UDS public keys";
 }
 
+/**
+ * Suppose that there is a StrongBox KeyMint instance on the device.
+ *
+ * Then, this test verifies that "keymint" is a substring of the component name in the configuration
+ * descriptor in the leaf certificate of the DICE chain for the primary ("default") KeyMint
+ * instance.
+ */
+// @VsrTest = 3.10-018.003
+TEST(NonParameterizedTests, componentNameInConfigurationDescriptorForPrimaryKeyMintInstance) {
+    int vendor_api_level = get_vendor_api_level();
+    if (vendor_api_level < 202504) {
+        GTEST_SKIP() << "Applies only to vendor API level >= 202504, but this device is: "
+                     << vendor_api_level;
+    }
+
+    if (!AServiceManager_isDeclared(KEYMINT_STRONGBOX_INSTANCE_NAME.c_str())) {
+        GTEST_SKIP() << "Strongbox is not present on this device.";
+    }
+
+    ::ndk::SpAIBinder binder(AServiceManager_waitForService(DEFAULT_INSTANCE_NAME.c_str()));
+    std::shared_ptr<IRemotelyProvisionedComponent> rpc =
+            IRemotelyProvisionedComponent::fromBinder(binder);
+    ASSERT_NE(rpc, nullptr);
+
+    bytevec challenge = randomBytes(64);
+    bytevec csr;
+    auto status = rpc->generateCertificateRequestV2({} /* keysToSign */, challenge, &csr);
+    EXPECT_TRUE(status.isOk()) << status.getDescription();
+
+    auto result = verifyComponentNameInKeyMintDiceChain(csr);
+    ASSERT_TRUE(result) << result.message();
+    ASSERT_TRUE(*result);
+}
+
 using GetHardwareInfoTests = VtsRemotelyProvisionedComponentTests;
 
 INSTANTIATE_REM_PROV_AIDL_TEST(GetHardwareInfoTests);
@@ -816,6 +849,37 @@
 };
 
 /**
+ * Check that ro.boot.vbmeta.device_state is not "locked" or ro.boot.verifiedbootstate
+ * is not "green" if and only if the mode on at least one certificate in the DICE chain
+ * is non-normal.
+ */
+TEST_P(CertificateRequestV2Test, unlockedBootloaderStatesImpliesNonnormalDiceChain) {
+    auto challenge = randomBytes(MAX_CHALLENGE_SIZE);
+    bytevec csr;
+    auto status =
+            provisionable_->generateCertificateRequestV2({} /* keysToSign */, challenge, &csr);
+    ASSERT_TRUE(status.isOk()) << status.getDescription();
+
+    auto isProper = isCsrWithProperDiceChain(csr, GetParam());
+    ASSERT_TRUE(isProper) << isProper.message();
+    if (!*isProper) {
+        GTEST_SKIP() << "Skipping test: Only a proper DICE chain has a mode set.";
+    }
+
+    auto nonNormalMode = hasNonNormalModeInDiceChain(csr, GetParam());
+    ASSERT_TRUE(nonNormalMode) << nonNormalMode.message();
+
+    auto deviceState = ::android::base::GetProperty("ro.boot.vbmeta.device_state", "");
+    auto verifiedBootState = ::android::base::GetProperty("ro.boot.verifiedbootstate", "");
+
+    ASSERT_EQ(deviceState != "locked" || verifiedBootState != "green", *nonNormalMode)
+            << "ro.boot.vbmeta.device_state = '" << deviceState
+            << "' and ro.boot.verifiedbootstate = '" << verifiedBootState << "', but it is "
+            << *nonNormalMode
+            << " that the DICE chain has a certificate with a non-normal mode set.";
+}
+
+/**
  * Generate an empty certificate request with all possible length of challenge, and decrypt and
  * verify the structure and content.
  */
@@ -1091,10 +1155,10 @@
 TEST_P(VsrRequirementTest, VsrEnforcementTest) {
     RpcHardwareInfo hwInfo;
     ASSERT_TRUE(provisionable_->getHardwareInfo(&hwInfo).isOk());
-    int vsr_api_level = get_vsr_api_level();
-    if (vsr_api_level < 34) {
-        GTEST_SKIP() << "Applies only to VSR API level 34 or newer, this device is: "
-                     << vsr_api_level;
+    int vendor_api_level = get_vendor_api_level();
+    if (vendor_api_level < __ANDROID_API_U__) {
+        GTEST_SKIP() << "Applies only to vendor API level >= 34, but this device is: "
+                     << vendor_api_level;
     }
     EXPECT_GE(hwInfo.versionNumber, 3)
             << "VSR 14+ requires IRemotelyProvisionedComponent v3 or newer.";
diff --git a/security/secretkeeper/aidl/vts/dice_sample.rs b/security/secretkeeper/aidl/vts/dice_sample.rs
index 4ef396a..d6379e5 100644
--- a/security/secretkeeper/aidl/vts/dice_sample.rs
+++ b/security/secretkeeper/aidl/vts/dice_sample.rs
@@ -283,7 +283,7 @@
             SUBCOMPONENT_AUTHORITY_HASH => hex::decode("ed255ae9ea98826f3f3a966849f0aaaf356d140c766a869048016e0ba10141af039fec5c53658ddebdad9c2339587c5ef5487bde89237ca79802238d91aebba8").unwrap(),
         },
         {
-            SUBCOMPONENT_NAME => "apex:com.android.btservices",
+            SUBCOMPONENT_NAME => "apex:com.android.bt",
             SUBCOMPONENT_SECURITY_VERSION => 990090000,
             SUBCOMPONENT_CODE_HASH => hex::decode("d7aa86dfdf92e662d2210cd2b3ad4e4522c917e9e287268363aa90e20f9ae16c").unwrap(),
             SUBCOMPONENT_AUTHORITY_HASH => hex::decode("a0d577d4a56cfad09aaa7abcd2355cd78872df85672f2faf9ac2fdf15c06147394e704c7473f28bed737803581a3d097275cc26d8095a4a896ee76167f9ee40e").unwrap(),
diff --git a/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl b/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
index b31a06c..99eb761 100644
--- a/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
+++ b/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
@@ -64,7 +64,7 @@
     int keySizeBytes;
   }
   union DerivedKeyPolicy {
-    android.hardware.security.see.hwcrypto.IHwCryptoKey.ClearKeyPolicy clearKey;
+    android.hardware.security.see.hwcrypto.IHwCryptoKey.ClearKeyPolicy clearKeyPolicy;
     byte[] opaqueKey;
   }
   parcelable DerivedKeyParameters {
diff --git a/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl b/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl
index 7c87dd3..3adb2f9 100644
--- a/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl
+++ b/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl
@@ -34,5 +34,5 @@
 package android.hardware.security.see.hwcrypto;
 @VintfStability
 interface IHwCryptoOperations {
-  android.hardware.security.see.hwcrypto.CryptoOperationResult[] processCommandList(inout android.hardware.security.see.hwcrypto.CryptoOperationSet[] operations, out android.hardware.security.see.hwcrypto.CryptoOperationErrorAdditionalInfo additionalErrorInfo);
+  android.hardware.security.see.hwcrypto.CryptoOperationResult[] processCommandList(inout android.hardware.security.see.hwcrypto.CryptoOperationSet[] operations);
 }
diff --git a/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl b/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
index 97a4c37..93d6cbc 100644
--- a/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
+++ b/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
@@ -107,10 +107,12 @@
          * If used we will derive a clear key and pass it back as an array of bytes on
          * <code>HwCryptoKeyMaterial::explicitKey</code>.
          */
-        ClearKeyPolicy clearKey;
+        ClearKeyPolicy clearKeyPolicy;
 
         /*
          * Policy for the newly derived opaque key. Defines how the key can be used and its type.
+         * Its definition can be found in <code>KeyPolicy.cddl</code>, which is basically a CBOR
+         * serialization of the file <code>KeyPolicy.aidl</code>.
          */
         byte[] opaqueKey;
     }
@@ -154,11 +156,14 @@
      *     Key to be used to derive the new key using HKDF.
      *
      * @return:
-     *     A DiceCurrentBoundKeyResult containint the versioned key tied the current client version
+     *     A DiceCurrentBoundKeyResult containing the versioned key tied the current client version
      *     on success.
      *
      * @throws:
-     *      ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs.
+     *      ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs,
+     *      in particular:
+     *          - BAD_PARAMETER if an invalid DeviceKeyId is requested.
+     *          - INVALID_KEY if an opaque key is provided that is not suitable for key derivation.
      */
     DiceCurrentBoundKeyResult deriveCurrentDicePolicyBoundKey(
             in DiceBoundDerivationKey derivationKey);
@@ -184,7 +189,11 @@
      *      success.
      *
      * @throws:
-     *      ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs.
+     *      ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs,
+     *      in particular:
+     *          - BAD_PARAMETER if an invalid DeviceKeyId is requested or if dicePolicyForKeyVersion
+     *            is not a valid encrypted DICE policy.
+     *          - INVALID_KEY if an opaque key is provided that is not suitable for key derivation.
      */
     DiceBoundKeyResult deriveDicePolicyBoundKey(
             in DiceBoundDerivationKey derivationKey, in byte[] dicePolicyForKeyVersion);
@@ -197,10 +206,15 @@
      *      file for more information.
      *
      * @return:
-     *      A HwCryptoKeyMaterial containing the derived key on success.
+     *      A <code>DerivedKey</code> containing the derived key on success.
      *
      * @throws:
-     *      ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs.
+     *      ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs,
+     *      in particular:
+     *          - BAD_PARAMETER if an invalid key policy is provided or if the key policy conflicts
+     *            with the requested key.
+     *          - SERIALIZATION_ERROR if the provided key policy is not a valid CBOR key policy.
+     *          - INVALID_KEY if an opaque key is provided that is not suitable for key derivation.
      */
     DerivedKey deriveKey(in DerivedKeyParameters parameters);
 
@@ -233,7 +247,11 @@
      *      IOpaqueKey on success.
      *
      * @throws:
-     *      ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs.
+     *      ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs,
+     *      in particular:
+     *          - BAD_PARAMETER if an invalid Key policy is provided or if the key policy conflicts
+     *            with provided key material.
+     *          - ALLOCATION_ERROR if the system runs out of memory while carring out the operation.
      */
     IOpaqueKey importClearKey(in ExplicitKeyMaterial keyMaterial, in KeyPolicy newKeyPolicy);
 
@@ -248,7 +266,9 @@
      * passing the receiver DICE policy to insure that only that receiver can import the key.
      *
      * @return:
-     *      byte[] on success, which is the caller encrypted DICE policy.
+     *      byte[] on success, which is the caller encrypted DICE policy. The DICE policy follows
+     *      the structure defined on DicePolicy.cddl, located under
+     *      hardware/interfaces/security/authgraph/aidl/android/hardware/security/authgraph/
      */
     byte[] getCurrentDicePolicy();
 
@@ -266,10 +286,14 @@
      *      DICE policy used to seal the exported key.
      *
      * @return:
-     *      An IOpaqueKey that can be directly be used on the local HWCrypto service on success.
+     *      An IOpaqueKey that can be directly used on the local HWCrypto service on success.
      *
      * @throws:
-     *      ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs.
+     *      ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs,
+     *      in particular:
+     *          - BAD_PARAMETER if an invalid encrypted sealing DICE policy is provided.
+     *          - ALLOCATION_ERROR if the system runs out of memory while carring out the operation.
+     *          - UNAUTHORIZED if the sealingDicePolicy do not match the caller.
      */
     IOpaqueKey keyTokenImport(in OpaqueKeyToken requestedKey, in byte[] sealingDicePolicy);
 
@@ -287,8 +311,9 @@
      *      An IOpaqueKey corresponding to the requested key slot on success.
      *
      * @throws:
-     *      ServiceSpecificException <code>UNAUTHORIZED</code> if the caller cannot access the
-     *      requested key, another specific error based on <code>HalErrorCode</code> otherwise.
+     *      ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs,
+     *      in particular:
+     *          - UNAUTHORIZED if the caller cannot access the requested key.
      */
     IOpaqueKey getKeyslotData(KeySlot slotId);
 }
diff --git a/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl b/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl
index 9df6d67..dbe4d80 100644
--- a/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl
+++ b/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl
@@ -31,19 +31,21 @@
      *
      * @param operations:
      *      Parameter containing 1 or more set of commands to execute. Additionally, each set can
-     *      also contain a context on which the commands will be executed.
-     *
-     * @param additionalErrorInfo:
-     *      Structure containing additional info when errors are encountered. Only valid if the
-     *      function failed its execution.
+     *      also contain a context on which the commands will be executed. The parameter has type
+     *      inout because it can contain buffers used to write the output of the operation.
      *
      * @return:
      *      CryptoOperationResult[] on success, which can contain a context to continue executing
      *      each of the provided operations sets.
      *
      * @throws:
-     *      ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs.
+     *      ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs,
+     *      in particular:
+     *          - INVALID_KEY if the provided key is not compatible with the operation requested.
+     *          - BAD_STATE if the provided <code>CryptoOperationSet</code> contains operations that
+     *            cannot be carried out in the current server state.
+     *          - UNSUPPORTED if the requested operation is not supported by the server.
+     *          - ALLOCATION_ERROR if the system runs out of memory while carring out the operation.
      */
-    CryptoOperationResult[] processCommandList(inout CryptoOperationSet[] operations,
-            out CryptoOperationErrorAdditionalInfo additionalErrorInfo);
+    CryptoOperationResult[] processCommandList(inout CryptoOperationSet[] operations);
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/AmbientBacklightColorFormat.aidl
similarity index 89%
rename from wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl
rename to tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/AmbientBacklightColorFormat.aidl
index 0600def..9ee9dcc 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl
+++ b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/AmbientBacklightColorFormat.aidl
@@ -31,11 +31,8 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.wifi.supplicant;
-@Backing(type="int") @VintfStability
-enum UsdReasonCode {
-  FAILURE_UNKNOWN = 0,
-  TIMEOUT = 1,
-  USER_REQUESTED = 2,
-  INVALID_ARGS = 3,
+package android.hardware.tv.mediaquality;
+@VintfStability
+union AmbientBacklightColorFormat {
+  int RGB888;
 }
diff --git a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/AmbientBacklightMetadata.aidl b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/AmbientBacklightMetadata.aidl
index bbdfd62..2ea3198 100644
--- a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/AmbientBacklightMetadata.aidl
+++ b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/AmbientBacklightMetadata.aidl
@@ -36,5 +36,5 @@
 parcelable AmbientBacklightMetadata {
   android.hardware.tv.mediaquality.AmbientBacklightSettings settings;
   android.hardware.tv.mediaquality.AmbientBacklightCompressAlgorithm compressAlgorithm;
-  int[] zonesColors;
+  android.hardware.tv.mediaquality.AmbientBacklightColorFormat[] zonesColors;
 }
diff --git a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/AmbientBacklightSettings.aidl b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/AmbientBacklightSettings.aidl
index ffbae26..7770e18 100644
--- a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/AmbientBacklightSettings.aidl
+++ b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/AmbientBacklightSettings.aidl
@@ -34,12 +34,12 @@
 package android.hardware.tv.mediaquality;
 @VintfStability
 parcelable AmbientBacklightSettings {
-  String packageName;
+  int uid;
   android.hardware.tv.mediaquality.AmbientBacklightSource source;
   int maxFramerate;
   android.hardware.graphics.common.PixelFormat colorFormat;
   int hZonesNumber;
   int vZonesNumber;
   boolean hasLetterbox;
-  int threshold;
+  int colorThreshold;
 }
diff --git a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/DigitalOutput.aidl b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/DigitalOutput.aidl
index a6e8b91..dad0e96 100644
--- a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/DigitalOutput.aidl
+++ b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/DigitalOutput.aidl
@@ -37,4 +37,7 @@
   AUTO,
   BYPASS,
   PCM,
+  DolbyDigitalPlus,
+  DolbyDigital,
+  DolbyMat,
 }
diff --git a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/DolbyAudioProcessing.aidl b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/DolbyAudioProcessing.aidl
index c29ae18..f21243c 100644
--- a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/DolbyAudioProcessing.aidl
+++ b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/DolbyAudioProcessing.aidl
@@ -37,11 +37,14 @@
   android.hardware.tv.mediaquality.DolbyAudioProcessing.SoundMode soundMode;
   boolean volumeLeveler;
   boolean surroundVirtualizer;
-  boolean doblyAtmos;
+  boolean dolbyAtmos;
   enum SoundMode {
     GAME,
     MOVIE,
     MUSIC,
     NEWS,
+    STADIUM,
+    STANDARD,
+    USER,
   }
 }
diff --git a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/IMediaQuality.aidl b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/IMediaQuality.aidl
index b569baa..26df461 100644
--- a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/IMediaQuality.aidl
+++ b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/IMediaQuality.aidl
@@ -34,7 +34,7 @@
 package android.hardware.tv.mediaquality;
 @VintfStability
 interface IMediaQuality {
-  void setCallback(in android.hardware.tv.mediaquality.IMediaQualityCallback callback);
+  void setAmbientBacklightCallback(in android.hardware.tv.mediaquality.IMediaQualityCallback callback);
   void setAmbientBacklightDetector(in android.hardware.tv.mediaquality.AmbientBacklightSettings settings);
   void setAmbientBacklightDetectionEnabled(in boolean enabled);
   boolean getAmbientBacklightDetectionEnabled();
@@ -55,6 +55,4 @@
   void sendDefaultSoundParameters(in android.hardware.tv.mediaquality.SoundParameters soundParameters);
   void getParamCaps(in android.hardware.tv.mediaquality.ParameterName[] paramNames, out android.hardware.tv.mediaquality.ParamCapability[] caps);
   void getVendorParamCaps(in android.hardware.tv.mediaquality.VendorParameterIdentifier[] names, out android.hardware.tv.mediaquality.VendorParamCapability[] caps);
-  void sendPictureParameters(in android.hardware.tv.mediaquality.PictureParameters pictureParameters);
-  void sendSoundParameters(in android.hardware.tv.mediaquality.SoundParameters soundParameters);
 }
diff --git a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/IPictureProfileAdjustmentListener.aidl b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/IPictureProfileAdjustmentListener.aidl
index e1a882e..1923043 100644
--- a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/IPictureProfileAdjustmentListener.aidl
+++ b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/IPictureProfileAdjustmentListener.aidl
@@ -37,5 +37,6 @@
   oneway void onPictureProfileAdjusted(in android.hardware.tv.mediaquality.PictureProfile pictureProfile);
   oneway void onParamCapabilityChanged(long pictureProfileId, in android.hardware.tv.mediaquality.ParamCapability[] caps);
   oneway void onVendorParamCapabilityChanged(long pictureProfileId, in android.hardware.tv.mediaquality.VendorParamCapability[] caps);
-  oneway void onRequestPictureParameters(long pictureProfileId);
+  oneway void requestPictureParameters(long pictureProfileId);
+  oneway void onStreamStatusChanged(long pictureProfileId, android.hardware.tv.mediaquality.StreamStatus status);
 }
diff --git a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/ISoundProfileAdjustmentListener.aidl b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/ISoundProfileAdjustmentListener.aidl
index e162601..d976cf7 100644
--- a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/ISoundProfileAdjustmentListener.aidl
+++ b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/ISoundProfileAdjustmentListener.aidl
@@ -37,5 +37,5 @@
   oneway void onSoundProfileAdjusted(in android.hardware.tv.mediaquality.SoundProfile soundProfile);
   oneway void onParamCapabilityChanged(long soundProfileId, in android.hardware.tv.mediaquality.ParamCapability[] caps);
   oneway void onVendorParamCapabilityChanged(long soundProfileId, in android.hardware.tv.mediaquality.VendorParamCapability[] caps);
-  oneway void onRequestSoundParameters(long SoundProfileId);
+  oneway void requestSoundParameters(long SoundProfileId);
 }
diff --git a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/ParameterName.aidl b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/ParameterName.aidl
index 711e270..522b8e6 100644
--- a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/ParameterName.aidl
+++ b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/ParameterName.aidl
@@ -61,6 +61,58 @@
   GLOBE_DIMMING,
   AUTO_PICTUREQUALITY_ENABLED,
   AUTO_SUPER_RESOLUTION_ENABLED,
+  LEVEL_RANGE,
+  GAMUT_MAPPING,
+  PC_MODE,
+  LOW_LATENCY,
+  VRR,
+  CVRR,
+  HDMI_RGB_RANGE,
+  COLOR_SPACE,
+  PANEL_INIT_MAX_LUMINCE_VALID,
+  GAMMA,
+  COLOR_TEMPERATURE_RED_GAIN,
+  COLOR_TEMPERATURE_GREEN_GAIN,
+  COLOR_TEMPERATURE_BLUE_GAIN,
+  COLOR_TEMPERATURE_RED_OFFSET,
+  COLOR_TEMPERATURE_GREEN_OFFSET,
+  COLOR_TEMPERATURE_BLUE_OFFSET,
+  ELEVEN_POINT_RED,
+  ELEVEN_POINT_GREEN,
+  ELEVEN_POINT_BLUE,
+  LOW_BLUE_LIGHT,
+  LD_MODE,
+  OSD_RED_GAIN,
+  OSD_GREEN_GAIN,
+  OSD_BLUE_GAIN,
+  OSD_RED_OFFSET,
+  OSD_GREEN_OFFSET,
+  OSD_BLUE_OFFSET,
+  OSD_HUE,
+  OSD_SATURATION,
+  OSD_CONTRAST,
+  COLOR_TUNER_SWITCH,
+  COLOR_TUNER_HUE_RED,
+  COLOR_TUNER_HUE_GREEN,
+  COLOR_TUNER_HUE_BLUE,
+  COLOR_TUNER_HUE_CYAN,
+  COLOR_TUNER_HUE_MAGENTA,
+  COLOR_TUNER_HUE_YELLOW,
+  COLOR_TUNER_HUE_FLESH,
+  COLOR_TUNER_SATURATION_RED,
+  COLOR_TUNER_SATURATION_GREEN,
+  COLOR_TUNER_SATURATION_BLUE,
+  COLOR_TUNER_SATURATION_CYAN,
+  COLOR_TUNER_SATURATION_MAGENTA,
+  COLOR_TUNER_SATURATION_YELLOW,
+  COLOR_TUNER_SATURATION_FLESH,
+  COLOR_TUNER_LUMINANCE_RED,
+  COLOR_TUNER_LUMINANCE_GREEN,
+  COLOR_TUNER_LUMINANCE_BLUE,
+  COLOR_TUNER_LUMINANCE_CYAN,
+  COLOR_TUNER_LUMINANCE_MAGENTA,
+  COLOR_TUNER_LUMINANCE_YELLOW,
+  COLOR_TUNER_LUMINANCE_FLESH,
   BALANCE,
   BASS,
   TREBLE,
@@ -77,4 +129,5 @@
   DTS_VIRTUAL_X,
   DIGITAL_OUTPUT,
   DIGITAL_OUTPUT_DELAY_MS,
+  SOUND_STYLE,
 }
diff --git a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/PictureParameter.aidl b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/PictureParameter.aidl
index c38e96f..3a9e4e4 100644
--- a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/PictureParameter.aidl
+++ b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/PictureParameter.aidl
@@ -71,4 +71,48 @@
   int panelInitMaxLuminceNits;
   boolean panelInitMaxLuminceValid;
   android.hardware.tv.mediaquality.Gamma gamma;
+  int colorTemperatureRedGain;
+  int colorTemperatureGreenGain;
+  int colorTemperatureBlueGain;
+  int colorTemperatureRedOffset;
+  int colorTemperatureGreenOffset;
+  int colorTemperatureBlueOffset;
+  int[11] elevenPointRed;
+  int[11] elevenPointGreen;
+  int[11] elevenPointBlue;
+  android.hardware.tv.mediaquality.QualityLevel lowBlueLight;
+  android.hardware.tv.mediaquality.QualityLevel LdMode;
+  int osdRedGain;
+  int osdGreenGain;
+  int osdBlueGain;
+  int osdRedOffset;
+  int osdGreenOffset;
+  int osdBlueOffset;
+  int osdHue;
+  int osdSaturation;
+  int osdContrast;
+  boolean colorTunerSwitch;
+  int colorTunerHueRed;
+  int colorTunerHueGreen;
+  int colorTunerHueBlue;
+  int colorTunerHueCyan;
+  int colorTunerHueMagenta;
+  int colorTunerHueYellow;
+  int colorTunerHueFlesh;
+  int colorTunerSaturationRed;
+  int colorTunerSaturationGreen;
+  int colorTunerSaturationBlue;
+  int colorTunerSaturationCyan;
+  int colorTunerSaturationMagenta;
+  int colorTunerSaturationYellow;
+  int colorTunerSaturationFlesh;
+  int colorTunerLuminanceRed;
+  int colorTunerLuminanceGreen;
+  int colorTunerLuminanceBlue;
+  int colorTunerLuminanceCyan;
+  int colorTunerLuminanceMagenta;
+  int colorTunerLuminanceYellow;
+  int colorTunerLuminanceFlesh;
+  boolean activeProfile;
+  android.hardware.tv.mediaquality.PictureQualityEventType pictureQualityEventType;
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/PictureQualityEventType.aidl
similarity index 86%
copy from wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl
copy to tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/PictureQualityEventType.aidl
index 0600def..11001f6 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl
+++ b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/PictureQualityEventType.aidl
@@ -31,11 +31,15 @@
 // 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.wifi.supplicant;
-@Backing(type="int") @VintfStability
-enum UsdReasonCode {
-  FAILURE_UNKNOWN = 0,
-  TIMEOUT = 1,
-  USER_REQUESTED = 2,
-  INVALID_ARGS = 3,
+package android.hardware.tv.mediaquality;
+@VintfStability
+enum PictureQualityEventType {
+  NONE,
+  BBD_RESULT,
+  VIDEO_DELAY_CHANGE,
+  CAPTUREPOINT_INFO_CHANGE,
+  VIDEOPATH_CHANGE,
+  EXTRA_FRAME_CHANGE,
+  DOLBY_IQ_CHANGE,
+  DOLBY_APO_CHANGE,
 }
diff --git a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/SoundParameter.aidl b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/SoundParameter.aidl
index 63eb55f..8c0eaaf 100644
--- a/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/SoundParameter.aidl
+++ b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/SoundParameter.aidl
@@ -50,4 +50,6 @@
   @nullable android.hardware.tv.mediaquality.DtsVirtualX dtsVirtualX;
   android.hardware.tv.mediaquality.DigitalOutput digitalOutput;
   int digitalOutputDelayMs;
+  boolean activeProfile;
+  android.hardware.tv.mediaquality.SoundStyle soundStyle;
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/SoundStyle.aidl
similarity index 89%
copy from wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl
copy to tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/SoundStyle.aidl
index 0600def..1dbaf2c 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl
+++ b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/SoundStyle.aidl
@@ -31,11 +31,15 @@
 // 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.wifi.supplicant;
-@Backing(type="int") @VintfStability
-enum UsdReasonCode {
-  FAILURE_UNKNOWN = 0,
-  TIMEOUT = 1,
-  USER_REQUESTED = 2,
-  INVALID_ARGS = 3,
+package android.hardware.tv.mediaquality;
+@VintfStability
+enum SoundStyle {
+  USER,
+  STANDARD,
+  VIVID,
+  SPORTS,
+  MOVIE,
+  MUSIC,
+  NEWS,
+  AUTO,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/StreamStatus.aidl
similarity index 82%
copy from wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl
copy to tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/StreamStatus.aidl
index 0600def..23e584b 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdReasonCode.aidl
+++ b/tv/mediaquality/aidl/aidl_api/android.hardware.tv.mediaquality/current/android/hardware/tv/mediaquality/StreamStatus.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,11 +31,24 @@
 // 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.wifi.supplicant;
-@Backing(type="int") @VintfStability
-enum UsdReasonCode {
-  FAILURE_UNKNOWN = 0,
-  TIMEOUT = 1,
-  USER_REQUESTED = 2,
-  INVALID_ARGS = 3,
+package android.hardware.tv.mediaquality;
+@VintfStability
+enum StreamStatus {
+  SDR,
+  DOLBYVISION,
+  HDR10,
+  TCH,
+  HLG,
+  HDR10PLUS,
+  HDRVIVID,
+  IMAXSDR,
+  IMAXHDR10,
+  IMAXHDR10PLUS,
+  FMMSDR,
+  FMMHDR10,
+  FMMHDR10PLUS,
+  FMMHLG,
+  FMMDOLBY,
+  FMMTCH,
+  FMMHDRVIVID,
 }
diff --git a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/AmbientBacklightColorFormat.aidl b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/AmbientBacklightColorFormat.aidl
new file mode 100644
index 0000000..f29de45
--- /dev/null
+++ b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/AmbientBacklightColorFormat.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tv.mediaquality;
+
+@VintfStability
+union AmbientBacklightColorFormat {
+    /**
+     * The color format is RGB888.
+     */
+    int RGB888;
+}
diff --git a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/AmbientBacklightMetadata.aidl b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/AmbientBacklightMetadata.aidl
index 49b3a28..d2599bd 100644
--- a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/AmbientBacklightMetadata.aidl
+++ b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/AmbientBacklightMetadata.aidl
@@ -16,6 +16,7 @@
 
 package android.hardware.tv.mediaquality;
 
+import android.hardware.tv.mediaquality.AmbientBacklightColorFormat;
 import android.hardware.tv.mediaquality.AmbientBacklightCompressAlgorithm;
 import android.hardware.tv.mediaquality.AmbientBacklightSettings;
 
@@ -32,8 +33,7 @@
     AmbientBacklightCompressAlgorithm compressAlgorithm;
 
     /**
-     * The colors for the zones. Format of the color will be indicated in the
-     * AmbientBacklightSettings::colorFormat.
+     * The colors for the zones. Formats of the color will be AmbientBacklightColorFormat.
      */
-    int[] zonesColors;
+    AmbientBacklightColorFormat[] zonesColors;
 }
diff --git a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/AmbientBacklightSettings.aidl b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/AmbientBacklightSettings.aidl
index b6a26ee..79bf02b 100644
--- a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/AmbientBacklightSettings.aidl
+++ b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/AmbientBacklightSettings.aidl
@@ -24,7 +24,7 @@
     /**
      * The package name of the ambient backlight control application.
      */
-    String packageName;
+    int uid;
 
     /**
      * The source of the ambient backlight.
@@ -42,12 +42,12 @@
     PixelFormat colorFormat;
 
     /**
-     * The number of zones in horizontal direction.
+     * The number of logical zones in horizontal direction desire by the package.
      */
     int hZonesNumber;
 
     /**
-     * The number of zones in vertical direction.
+     * The number of logical zones in vertical direction desire by the package.
      */
     int vZonesNumber;
 
@@ -66,5 +66,5 @@
      * the colorFormat. For example, RGB888, where the values of R/G/B range from 0 to 255,
      * and the threshold is a positive number within the same range.
      */
-    int threshold;
+    int colorThreshold;
 }
diff --git a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/DigitalOutput.aidl b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/DigitalOutput.aidl
index 1a46ae6..a58ad79 100644
--- a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/DigitalOutput.aidl
+++ b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/DigitalOutput.aidl
@@ -22,6 +22,8 @@
      * Automatically selects the best audio format to send to the connected audio device
      * based on the incoming audio stream. This mode prioritizes high-quality formats
      * like Dolby Digital or DTS if supported by the device, otherwise falls back to PCM.
+     *
+     * This is the default value for digital output.
      */
     AUTO,
 
@@ -38,4 +40,23 @@
      * range of devices but sacrifices surround sound capabilities.
      */
     PCM,
+
+    /**
+     * Dolby Digital Plus (E-AC-3) output. An enhanced version of Dolby Digital
+     * supporting more channels (up to 7.1 surround sound) and higher bitrates.
+     * Commonly used for streaming and online content.
+     */
+    DolbyDigitalPlus,
+
+    /**
+     * Dolby Digital (AC-3) output. Provides up to 5.1 channels
+     * of surround sound, a standard for DVDs, Blu-rays, and TV broadcasts.
+     */
+    DolbyDigital,
+
+    /**
+     * Dolby Multistream Audio (MAT) output. Carries multiple audio streams
+     * (e.g., different languages, audio descriptions) within a Dolby Digital Plus bitstream.
+     */
+    DolbyMat,
 }
diff --git a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/DolbyAudioProcessing.aidl b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/DolbyAudioProcessing.aidl
index d56848c..3454556 100644
--- a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/DolbyAudioProcessing.aidl
+++ b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/DolbyAudioProcessing.aidl
@@ -18,11 +18,15 @@
 
 @VintfStability
 parcelable DolbyAudioProcessing {
+    /* The default value for sound mode is standard. */
     enum SoundMode {
         GAME,
         MOVIE,
         MUSIC,
         NEWS,
+        STADIUM,
+        STANDARD,
+        USER,
     }
 
     /**
@@ -60,5 +64,5 @@
      * mixed in Dolby Atmos and a compatible sound system with upward-firing speakers
      * or a Dolby Atmos soundbar.
      */
-    boolean doblyAtmos;
+    boolean dolbyAtmos;
 }
diff --git a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/IMediaQuality.aidl b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/IMediaQuality.aidl
index 373a977..3da1495 100644
--- a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/IMediaQuality.aidl
+++ b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/IMediaQuality.aidl
@@ -39,7 +39,7 @@
      *
      * @param callback Callback object to pass events.
      */
-    void setCallback(in IMediaQualityCallback callback);
+    void setAmbientBacklightCallback(in IMediaQualityCallback callback);
 
     /**
      * Sets the ambient backlight detector settings.
@@ -55,6 +55,9 @@
      * device as configured before.
      *
      * @param enabled True to enable the ambient backlight detection, false to disable.
+     *
+     * @return Status::ok on success
+     *         UNSUPPORTED_OPERATION if this functionality is unsupported.
      */
     void setAmbientBacklightDetectionEnabled(in boolean enabled);
 
@@ -88,6 +91,9 @@
      * parameters depends on the current content playing.
      *
      * @param enable True to enable, false to disable.
+     *
+     * @return Status::ok on success
+     *         UNSUPPORTED_OPERATION if this functionality is unsupported.
      */
     void setAutoPqEnabled(boolean enable);
 
@@ -112,6 +118,9 @@
      * lower resolution image and invent the missing pixel to make the image looks sharper.
      *
      * @param enable True to enable, false to disable.
+     *
+     * @return Status::ok on success
+     *         UNSUPPORTED_OPERATION if this functionality is unsupported.
      */
     void setAutoSrEnabled(boolean enable);
 
@@ -136,6 +145,9 @@
      * adjust the sound parameters depends on the current content playing.
      *
      * @param enable True to enable, false to disable.
+     *
+     * @return Status::ok on success
+     *         UNSUPPORTED_OPERATION if this functionality is unsupported.
      */
     void setAutoAqEnabled(boolean enable);
 
@@ -207,20 +219,4 @@
      * Gets vendor capability information of the given parameters.
      */
     void getVendorParamCaps(in VendorParameterIdentifier[] names, out VendorParamCapability[] caps);
-
-    /**
-     * When HAL request picture parameters by picture profile id, the framework will use this to
-     * send the picture parameters associate with the profile id.
-     *
-     * @param pictureParameters pictureParameters that associate with the profile id HAL provided.
-     */
-    void sendPictureParameters(in PictureParameters pictureParameters);
-
-    /**
-     * When HAL request sound parameters by sound profile id, the framework will use this to
-     * send the sound parameters associate with the profile id.
-     *
-     * @param soundParameters soundParameters that associate with the profile id HAL provided.
-     */
-    void sendSoundParameters(in SoundParameters soundParameters);
 }
diff --git a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/IPictureProfileAdjustmentListener.aidl b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/IPictureProfileAdjustmentListener.aidl
index 06651e4..0e11fb0 100644
--- a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/IPictureProfileAdjustmentListener.aidl
+++ b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/IPictureProfileAdjustmentListener.aidl
@@ -18,6 +18,7 @@
 
 import android.hardware.tv.mediaquality.ParamCapability;
 import android.hardware.tv.mediaquality.PictureProfile;
+import android.hardware.tv.mediaquality.StreamStatus;
 import android.hardware.tv.mediaquality.VendorParamCapability;
 
 @VintfStability
@@ -59,5 +60,14 @@
      *
      * @param pictureProfileId The PictureProfile id that associate with the PictureProfile.
      */
-    void onRequestPictureParameters(long pictureProfileId);
+    void requestPictureParameters(long pictureProfileId);
+
+    /**
+     * Notifies Media Quality Manager when stream status changed.
+     *
+     * @param pictureProfileId the ID of the profile used by the media content. -1 if there
+     *                         is no associated profile.
+     * @param status the status of the current stream.
+     */
+    void onStreamStatusChanged(long pictureProfileId, StreamStatus status);
 }
diff --git a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/IPictureProfileChangedListener.aidl b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/IPictureProfileChangedListener.aidl
index 35112e1..7fc7ab2 100644
--- a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/IPictureProfileChangedListener.aidl
+++ b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/IPictureProfileChangedListener.aidl
@@ -21,10 +21,9 @@
 @VintfStability
 oneway interface IPictureProfileChangedListener {
     /**
-     * Notifies the composer HAL that the picture profile has changed. For picture profile details,
-     * check PictureProfile.
+     * Notifies the service that the picture profile has changed.
      *
-     * @param pictureProfile Picture profile passed to the composer HAL.
+     * @param pictureProfile Picture profile that should be cached by the service.
      */
     void onPictureProfileChanged(in PictureProfile pictureProfile);
 }
diff --git a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/ISoundProfileAdjustmentListener.aidl b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/ISoundProfileAdjustmentListener.aidl
index 2ab9c6c..3f96762 100644
--- a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/ISoundProfileAdjustmentListener.aidl
+++ b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/ISoundProfileAdjustmentListener.aidl
@@ -59,5 +59,5 @@
      *
      * @param SoundProfileId The SoundProfile id that associate with the SoundProfile.
      */
-    void onRequestSoundParameters(long SoundProfileId);
+    void requestSoundParameters(long SoundProfileId);
 }
diff --git a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/ParameterName.aidl b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/ParameterName.aidl
index 0a3c97b..d451590 100644
--- a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/ParameterName.aidl
+++ b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/ParameterName.aidl
@@ -51,6 +51,58 @@
     GLOBE_DIMMING,
     AUTO_PICTUREQUALITY_ENABLED,
     AUTO_SUPER_RESOLUTION_ENABLED,
+    LEVEL_RANGE,
+    GAMUT_MAPPING,
+    PC_MODE,
+    LOW_LATENCY,
+    VRR,
+    CVRR,
+    HDMI_RGB_RANGE,
+    COLOR_SPACE,
+    PANEL_INIT_MAX_LUMINCE_VALID,
+    GAMMA,
+    COLOR_TEMPERATURE_RED_GAIN,
+    COLOR_TEMPERATURE_GREEN_GAIN,
+    COLOR_TEMPERATURE_BLUE_GAIN,
+    COLOR_TEMPERATURE_RED_OFFSET,
+    COLOR_TEMPERATURE_GREEN_OFFSET,
+    COLOR_TEMPERATURE_BLUE_OFFSET,
+    ELEVEN_POINT_RED,
+    ELEVEN_POINT_GREEN,
+    ELEVEN_POINT_BLUE,
+    LOW_BLUE_LIGHT,
+    LD_MODE,
+    OSD_RED_GAIN,
+    OSD_GREEN_GAIN,
+    OSD_BLUE_GAIN,
+    OSD_RED_OFFSET,
+    OSD_GREEN_OFFSET,
+    OSD_BLUE_OFFSET,
+    OSD_HUE,
+    OSD_SATURATION,
+    OSD_CONTRAST,
+    COLOR_TUNER_SWITCH,
+    COLOR_TUNER_HUE_RED,
+    COLOR_TUNER_HUE_GREEN,
+    COLOR_TUNER_HUE_BLUE,
+    COLOR_TUNER_HUE_CYAN,
+    COLOR_TUNER_HUE_MAGENTA,
+    COLOR_TUNER_HUE_YELLOW,
+    COLOR_TUNER_HUE_FLESH,
+    COLOR_TUNER_SATURATION_RED,
+    COLOR_TUNER_SATURATION_GREEN,
+    COLOR_TUNER_SATURATION_BLUE,
+    COLOR_TUNER_SATURATION_CYAN,
+    COLOR_TUNER_SATURATION_MAGENTA,
+    COLOR_TUNER_SATURATION_YELLOW,
+    COLOR_TUNER_SATURATION_FLESH,
+    COLOR_TUNER_LUMINANCE_RED,
+    COLOR_TUNER_LUMINANCE_GREEN,
+    COLOR_TUNER_LUMINANCE_BLUE,
+    COLOR_TUNER_LUMINANCE_CYAN,
+    COLOR_TUNER_LUMINANCE_MAGENTA,
+    COLOR_TUNER_LUMINANCE_YELLOW,
+    COLOR_TUNER_LUMINANCE_FLESH,
 
     BALANCE,
     BASS,
@@ -68,4 +120,5 @@
     DTS_VIRTUAL_X,
     DIGITAL_OUTPUT,
     DIGITAL_OUTPUT_DELAY_MS,
+    SOUND_STYLE,
 }
diff --git a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/PictureParameter.aidl b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/PictureParameter.aidl
index b7f2a11..2443d65 100644
--- a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/PictureParameter.aidl
+++ b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/PictureParameter.aidl
@@ -20,6 +20,7 @@
 import android.hardware.tv.mediaquality.ColorSpace;
 import android.hardware.tv.mediaquality.ColorTemperature;
 import android.hardware.tv.mediaquality.Gamma;
+import android.hardware.tv.mediaquality.PictureQualityEventType;
 import android.hardware.tv.mediaquality.QualityLevel;
 
 /**
@@ -253,4 +254,198 @@
 
     /* The gamma curve used for the display. */
     Gamma gamma;
+
+    /**
+     * The color gain value for color temperature adjustment.
+     * The value adjusts the intensity of color in the bright areas on the TV.
+     *
+     * The value range is from -100 to 100 where -100 would eliminate that color
+     * and 100 would significantly boost that color.
+     *
+     * The default/unmodified value is 0. No adjustment is applied to that color.
+     */
+    int colorTemperatureRedGain;
+
+    int colorTemperatureGreenGain;
+
+    int colorTemperatureBlueGain;
+
+    /**
+     * The color offset value for color temperature adjustment.
+     * This value adjusts the intensity of color in the dark areas on the TV.
+     *
+     * The value range is from -100 to 100 where -100 would eliminate that color
+     * and 100 would significantly boost that color.
+     *
+     * The default/unmodified value is 0. No adjustment is applied to that color.
+     */
+    int colorTemperatureRedOffset;
+
+    int colorTemperatureGreenOffset;
+
+    int colorTemperatureBlueOffset;
+
+    /**
+     * The parameters in this section is for 11-point white balance in advanced TV picture setting.
+     * 11-Point White Balance allows for very precise adjustment of the color temperature of the
+     * TV. It aims to make sure white looks truly white, without any unwanted color tints, across
+     * the entire range of brightness levels.
+     *
+     * The "11 points" refer to 11 different brightness levels from 0 (black) to 10 (white).
+     * At each of these points, we can fine-tune the mixture of red, green and blue to achieve
+     * neutral white.
+     *
+     * These parameters specifically control the amount of red, blue or green at each of the 11
+     * brightness points. The parameter type is an int array with a fix size of 11. The indexes
+     * 0 - 10 are the 11 different points. For example, elevenPointRed[0] adjusts the red level
+     * at the darkest black level. elevenPointRed[1] adjusts red at the next brightness level up,
+     * and so on.
+     *
+     * The value range is from 0 - 100 for each indexes, where 0 is the minimum intensity of
+     * that color(red, green, blue) at a specific brightness point and 100 is the maximum intensity
+     * of that color at that point.
+     *
+     * The default/unmodified value is 50. It can be other values depends on different TVs.
+     */
+    int[11] elevenPointRed;
+
+    int[11] elevenPointGreen;
+
+    int[11] elevenPointBlue;
+
+    /**
+     * Adjust gamma blue gain/offset.
+     *
+     * The default value is middle. Can be different depends on different TVs.
+     */
+    QualityLevel lowBlueLight;
+
+    /**
+     * Advance setting for local dimming level.
+     *
+     * The default value is off. Can be different depends on different TVs.
+     */
+    QualityLevel LdMode;
+
+    /**
+     * The parameter in this section is for on-screen display color gain and offset.
+     *
+     * Color gain is to adjust the intensity of that color (red, blue, green) in the brighter
+     * part of the image.
+     * Color offset is to adjust the intensity of that color in the darker part of the image.
+     *
+     * For example, increase OSD (on-screen display) red gain will make brighter reds even more
+     * intense, while decreasing it will make them less vibrant. Increase OSD red offset will add
+     * more red to the darker areas, while decreasing it will reduce the red in the shadows.
+     *
+     * The value range is from 0 to 2047. (11-bit resolution for the adjustment)
+     * The default value depends on different TVs.
+     */
+    int osdRedGain;
+
+    int osdGreenGain;
+
+    int osdBlueGain;
+
+    int osdRedOffset;
+
+    int osdGreenOffset;
+
+    int osdBlueOffset;
+
+    /* The value range is 0-100 */
+    int osdHue;
+
+    /* The value range is 0-255 */
+    int osdSaturation;
+
+    int osdContrast;
+
+    /**
+     * Enable/disable color tuner.
+     *
+     * The color tuner can adjust color temperature and picture color.
+     * The default is enabled.
+     */
+    boolean colorTunerSwitch;
+
+    /**
+     * The parameters in this section adjust the hue of each color.
+     *
+     * For example, increase colorTunerHueRed will make the image more purplish-red or more
+     * orange-red. increase colorTunerHueGreen will make the image more yellowish-green or
+     * more bluish-green. Flesh is a special one, it can make skin tones appear warmer (reddish)
+     * or cooler (more yellowish).
+     *
+     * The value range is from 0 - 100, and the default value is 50.
+     */
+    int colorTunerHueRed;
+
+    int colorTunerHueGreen;
+
+    int colorTunerHueBlue;
+
+    int colorTunerHueCyan;
+
+    int colorTunerHueMagenta;
+
+    int colorTunerHueYellow;
+
+    int colorTunerHueFlesh;
+
+    /**
+     * The parameters in this section adjust the saturation of each color.
+     *
+     * For example, increase colorTunerSaturationBlue will make the color blue more deeper
+     * and richer. Decrease will make the color blue more washed-out blues.
+     *
+     * The value range is from 0 -100, and the default value is 50.
+     */
+    int colorTunerSaturationRed;
+
+    int colorTunerSaturationGreen;
+
+    int colorTunerSaturationBlue;
+
+    int colorTunerSaturationCyan;
+
+    int colorTunerSaturationMagenta;
+
+    int colorTunerSaturationYellow;
+
+    int colorTunerSaturationFlesh;
+
+    /**
+     * The parameters in this section adjust the luminance (brightness) of each color.
+     *
+     * For example, increase colorTunerLuminanceRed will makes red appear brighter. Decrease
+     * will makes red appear darker.
+     *
+     * The value range is from 0 -100, and the default value is 50.
+     */
+    int colorTunerLuminanceRed;
+
+    int colorTunerLuminanceGreen;
+
+    int colorTunerLuminanceBlue;
+
+    int colorTunerLuminanceCyan;
+
+    int colorTunerLuminanceMagenta;
+
+    int colorTunerLuminanceYellow;
+
+    int colorTunerLuminanceFlesh;
+
+    /**
+     * Determines whether the current profile is actively in use or not.
+     */
+    boolean activeProfile;
+
+    /**
+     * This indicates non picture parameter status change about a profile.
+     *
+     * Those status can be found in PictureQualityEventType.
+     */
+    PictureQualityEventType pictureQualityEventType;
 }
diff --git a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/PictureQualityEventType.aidl b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/PictureQualityEventType.aidl
new file mode 100644
index 0000000..4985707
--- /dev/null
+++ b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/PictureQualityEventType.aidl
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tv.mediaquality;
+
+@VintfStability
+enum PictureQualityEventType {
+    /* No status change */
+    NONE,
+
+    /**
+     * Black bar detection Event.
+     *
+     * TV has detected or lost track of black bars, potentially triggering a change in aspect
+     * ratio.
+     */
+    BBD_RESULT,
+
+    /**
+     * Video delay change event.
+     *
+     * This signifies a change in the video processing delay, might due to enabling or disabling
+     * certain picture quality features.
+     */
+    VIDEO_DELAY_CHANGE,
+
+    /**
+     * Capture point change event.
+     *
+     * A change in video processing pipeline the image is being captured for display. Changes here
+     * relates to switching between different video sources.
+     */
+    CAPTUREPOINT_INFO_CHANGE,
+
+    /**
+     * Video path change event.
+     *
+     * Indicates a change in the video signal path. This could involve switching between
+     * different input sources.
+     */
+    VIDEOPATH_CHANGE,
+
+    /**
+     * Extra frame change event.
+     *
+     * Some TVs use techniques like frame interpolation (inserting extra frames) to smooth motion.
+     * Change means the function is turned on or off.
+     */
+    EXTRA_FRAME_CHANGE,
+
+    /**
+     * Dolby Vision IQ change event.
+     *
+     * Dolby Vision IQ is a technology that adjusts HDR video based on the ambient light in the
+     * room. A change means the function is turned on or off.
+     */
+    DOLBY_IQ_CHANGE,
+
+    /**
+     * Dolby Vision audio processing object change event.
+     *
+     * This event might be triggered by changes in audio settings that affect the picture quality,
+     * such as enabling or disabling a feature that synchronizes audio and video processing.
+     */
+    DOLBY_APO_CHANGE,
+}
diff --git a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/SoundParameter.aidl b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/SoundParameter.aidl
index 4714ad2..7a8a031 100644
--- a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/SoundParameter.aidl
+++ b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/SoundParameter.aidl
@@ -22,6 +22,7 @@
 import android.hardware.tv.mediaquality.DtsVirtualX;
 import android.hardware.tv.mediaquality.EqualizerDetail;
 import android.hardware.tv.mediaquality.QualityLevel;
+import android.hardware.tv.mediaquality.SoundStyle;
 
 /**
  * The parameters for Sound Profile.
@@ -92,4 +93,16 @@
 
     /* Digital output delay in ms. */
     int digitalOutputDelayMs;
+
+    /**
+     * Determines whether the current profile is actively in use or not.
+     */
+    boolean activeProfile;
+
+    /*
+     * Sound style of the profile.
+     *
+     * The default value is user customized profile.
+     */
+    SoundStyle soundStyle;
 }
diff --git a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/SoundStyle.aidl b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/SoundStyle.aidl
new file mode 100644
index 0000000..b8650d2
--- /dev/null
+++ b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/SoundStyle.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tv.mediaquality;
+
+@VintfStability
+enum SoundStyle {
+    /* User custom style is the default value for sound style */
+    USER,
+    STANDARD,
+    VIVID,
+    SPORTS,
+    MOVIE,
+    MUSIC,
+    NEWS,
+    AUTO,
+}
diff --git a/tv/mediaquality/aidl/android/hardware/tv/mediaquality/StreamStatus.aidl b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/StreamStatus.aidl
new file mode 100644
index 0000000..ec3b995
--- /dev/null
+++ b/tv/mediaquality/aidl/android/hardware/tv/mediaquality/StreamStatus.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tv.mediaquality;
+
+@VintfStability
+enum StreamStatus {
+    SDR,
+    DOLBYVISION,
+    HDR10,
+    TCH,
+    HLG,
+    HDR10PLUS,
+    HDRVIVID,
+    IMAXSDR,
+    IMAXHDR10,
+    IMAXHDR10PLUS,
+    FMMSDR,
+    FMMHDR10,
+    FMMHDR10PLUS,
+    FMMHLG,
+    FMMDOLBY,
+    FMMTCH,
+    FMMHDRVIVID,
+}
diff --git a/tv/mediaquality/aidl/default/hal/media_quality_hal_impl.rs b/tv/mediaquality/aidl/default/hal/media_quality_hal_impl.rs
index 1946cec..087730f 100644
--- a/tv/mediaquality/aidl/default/hal/media_quality_hal_impl.rs
+++ b/tv/mediaquality/aidl/default/hal/media_quality_hal_impl.rs
@@ -32,12 +32,14 @@
     VendorParameterIdentifier::VendorParameterIdentifier,
 };
 use binder::{Interface, Strong};
+use binder::ExceptionCode;
 use std::sync::{Arc, Mutex};
 use std::thread;
 
 /// Defined so we can implement the IMediaQuality AIDL interface.
 pub struct MediaQualityService {
     callback: Arc<Mutex<Option<Strong<dyn IMediaQualityCallback>>>>,
+    ambient_backlight_supported: Arc<Mutex<bool>>,
     ambient_backlight_enabled: Arc<Mutex<bool>>,
     ambient_backlight_detector_settings: Arc<Mutex<AmbientBacklightSettings>>,
     auto_pq_supported: Arc<Mutex<bool>>,
@@ -60,6 +62,7 @@
     pub fn new() -> Self {
         Self {
             callback: Arc::new(Mutex::new(None)),
+            ambient_backlight_supported: Arc::new(Mutex::new(false)),
             ambient_backlight_enabled: Arc::new(Mutex::new(true)),
             ambient_backlight_detector_settings:
                     Arc::new(Mutex::new(AmbientBacklightSettings::default())),
@@ -81,7 +84,7 @@
 
 impl IMediaQuality for MediaQualityService {
 
-    fn setCallback(
+    fn setAmbientBacklightCallback(
         &self,
         callback: &Strong<dyn IMediaQualityCallback>
     ) -> binder::Result<()> {
@@ -97,45 +100,51 @@
     ) -> binder::Result<()> {
         println!("Received settings: {:?}", settings);
         let mut ambient_backlight_detector_settings = self.ambient_backlight_detector_settings.lock().unwrap();
-        ambient_backlight_detector_settings.packageName = settings.packageName.clone();
+        ambient_backlight_detector_settings.uid = settings.uid.clone();
         ambient_backlight_detector_settings.source = settings.source;
         ambient_backlight_detector_settings.maxFramerate = settings.maxFramerate;
         ambient_backlight_detector_settings.colorFormat = settings.colorFormat;
         ambient_backlight_detector_settings.hZonesNumber = settings.hZonesNumber;
         ambient_backlight_detector_settings.vZonesNumber = settings.vZonesNumber;
         ambient_backlight_detector_settings.hasLetterbox = settings.hasLetterbox;
-        ambient_backlight_detector_settings.threshold = settings.threshold;
+        ambient_backlight_detector_settings.colorThreshold = settings.colorThreshold;
         Ok(())
     }
 
     fn setAmbientBacklightDetectionEnabled(&self, enabled: bool) -> binder::Result<()> {
         println!("Received enabled: {}", enabled);
         let mut ambient_backlight_enabled = self.ambient_backlight_enabled.lock().unwrap();
+        let ambient_backlight_supported = self.ambient_backlight_supported.lock().unwrap();
         *ambient_backlight_enabled = enabled;
-        if enabled {
-            println!("Enable Ambient Backlight detection");
-            thread::scope(|s| {
-                s.spawn(|| {
-                    let cb = self.callback.lock().unwrap();
-                    if let Some(cb) = &*cb {
-                        let enabled_event = AmbientBacklightEvent::Enabled(true);
-                        cb.notifyAmbientBacklightEvent(&enabled_event).unwrap();
-                    }
+
+        if *ambient_backlight_supported {
+            if enabled {
+                println!("Enable Ambient Backlight detection");
+                thread::scope(|s| {
+                    s.spawn(|| {
+                        let cb = self.callback.lock().unwrap();
+                        if let Some(cb) = &*cb {
+                            let enabled_event = AmbientBacklightEvent::Enabled(true);
+                            cb.notifyAmbientBacklightEvent(&enabled_event).unwrap();
+                        }
+                    });
                 });
-            });
+            } else {
+                println!("Disable Ambient Backlight detection");
+                thread::scope(|s| {
+                    s.spawn(|| {
+                        let cb = self.callback.lock().unwrap();
+                        if let Some(cb) = &*cb {
+                            let disabled_event = AmbientBacklightEvent::Enabled(false);
+                            cb.notifyAmbientBacklightEvent(&disabled_event).unwrap();
+                        }
+                    });
+                });
+            }
+            return Ok(());
         } else {
-            println!("Disable Ambient Backlight detection");
-            thread::scope(|s| {
-                s.spawn(|| {
-                    let cb = self.callback.lock().unwrap();
-                    if let Some(cb) = &*cb {
-                        let disabled_event = AmbientBacklightEvent::Enabled(false);
-                        cb.notifyAmbientBacklightEvent(&disabled_event).unwrap();
-                    }
-                });
-            });
+            return Err(ExceptionCode::UNSUPPORTED_OPERATION.into());
         }
-        Ok(())
     }
 
     fn getAmbientBacklightDetectionEnabled(&self) -> binder::Result<bool> {
@@ -155,13 +164,19 @@
 
     fn setAutoPqEnabled(&self, enabled: bool) -> binder::Result<()> {
         let mut auto_pq_enabled = self.auto_pq_enabled.lock().unwrap();
+        let auto_pq_supported = self.auto_pq_supported.lock().unwrap();
         *auto_pq_enabled = enabled;
-        if enabled {
-            println!("Enable auto picture quality");
+
+        if *auto_pq_supported {
+            if enabled {
+                println!("Enable auto picture quality");
+            } else {
+                println!("Disable auto picture quality");
+            }
+            return Ok(());
         } else {
-            println!("Disable auto picture quality");
+            return Err(ExceptionCode::UNSUPPORTED_OPERATION.into());
         }
-        Ok(())
     }
 
     fn isAutoSrSupported(&self) -> binder::Result<bool> {
@@ -176,13 +191,19 @@
 
     fn setAutoSrEnabled(&self, enabled: bool) -> binder::Result<()> {
         let mut auto_sr_enabled = self.auto_sr_enabled.lock().unwrap();
+        let auto_sr_supported = self.auto_sr_supported.lock().unwrap();
         *auto_sr_enabled = enabled;
-        if enabled {
-            println!("Enable auto super resolution");
+
+        if *auto_sr_supported {
+            if enabled {
+                println!("Enable auto super resolution");
+            } else {
+                println!("Disable auto super resolution");
+            }
+            return Ok(());
         } else {
-            println!("Disable auto super resolution");
+            return Err(ExceptionCode::UNSUPPORTED_OPERATION.into());
         }
-        Ok(())
     }
 
     fn isAutoAqSupported(&self) -> binder::Result<bool> {
@@ -197,13 +218,19 @@
 
     fn setAutoAqEnabled(&self, enabled: bool) -> binder::Result<()> {
         let mut auto_aq_enabled = self.auto_aq_enabled.lock().unwrap();
+        let auto_aq_supported = self.auto_aq_supported.lock().unwrap();
         *auto_aq_enabled = enabled;
-        if enabled {
-            println!("Enable auto audio quality");
+
+        if *auto_aq_supported {
+            if enabled {
+                println!("Enable auto audio quality");
+            } else {
+                println!("Disable auto audio quality");
+            }
+            return Ok(());
         } else {
-            println!("Disable auto audio quality");
+            return Err(ExceptionCode::UNSUPPORTED_OPERATION.into());
         }
-        Ok(())
     }
 
     fn getPictureProfileListener(&self) -> binder::Result<binder::Strong<dyn IPictureProfileChangedListener>> {
@@ -265,14 +292,4 @@
         println!("getVendorParamCaps. len= {}", param_names.len());
         Ok(())
     }
-
-    fn sendPictureParameters(&self, _picture_parameters: &PictureParameters) -> binder::Result<()>{
-        println!("Received picture parameters");
-        Ok(())
-    }
-
-    fn sendSoundParameters(&self, _sound_parameters: &SoundParameters) -> binder::Result<()>{
-        println!("Received sound parameters");
-        Ok(())
-    }
 }
diff --git a/tv/mediaquality/aidl/vts/functional/VtsHalMediaQualityTest.cpp b/tv/mediaquality/aidl/vts/functional/VtsHalMediaQualityTest.cpp
index 84f798b..a01d4b0 100644
--- a/tv/mediaquality/aidl/vts/functional/VtsHalMediaQualityTest.cpp
+++ b/tv/mediaquality/aidl/vts/functional/VtsHalMediaQualityTest.cpp
@@ -29,11 +29,13 @@
 #include <aidl/android/hardware/tv/mediaquality/SoundParameter.h>
 #include <aidl/android/hardware/tv/mediaquality/SoundParameters.h>
 #include <aidl/android/hardware/tv/mediaquality/SoundProfile.h>
+#include <aidl/android/hardware/tv/mediaquality/StreamStatus.h>
 
 #include <android/binder_auto_utils.h>
 #include <android/binder_manager.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
+#include <log/log.h>
 #include <future>
 
 using aidl::android::hardware::graphics::common::PixelFormat;
@@ -51,6 +53,7 @@
 using aidl::android::hardware::tv::mediaquality::SoundParameter;
 using aidl::android::hardware::tv::mediaquality::SoundParameters;
 using aidl::android::hardware::tv::mediaquality::SoundProfile;
+using aidl::android::hardware::tv::mediaquality::StreamStatus;
 using aidl::android::hardware::tv::mediaquality::VendorParamCapability;
 using aidl::android::hardware::tv::mediaquality::VendorParameterIdentifier;
 using android::ProcessState;
@@ -61,6 +64,407 @@
 #define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
 #define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
 
+void validateParameterRange0To100(int value) {
+    EXPECT_GE(value, 0);
+    EXPECT_LE(value, 100);
+}
+
+void validateParameterRange0To2047(int value) {
+    EXPECT_GE(value, 0);
+    EXPECT_LE(value, 2047);
+}
+
+void validateColorTemperature(int value) {
+    EXPECT_GE(value, -100);
+    EXPECT_LE(value, 100);
+}
+
+void validatePictureParameter(const PictureParameter& param) {
+    switch (param.getTag()) {
+        case PictureParameter::Tag::brightness: {
+            ALOGD("[validatePictureParameter] validate brightness value");
+            float value = param.get<PictureParameter::Tag::brightness>();
+            EXPECT_TRUE(value >= 0.0f && value <= 1.0f);
+            break;
+        }
+        case PictureParameter::Tag::contrast: {
+            ALOGD("[validatePictureParameter] validate contrast value");
+            int value = param.get<PictureParameter::Tag::contrast>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::sharpness: {
+            ALOGD("[validatePictureParameter] validate sharpness value");
+            int value = param.get<PictureParameter::Tag::sharpness>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::saturation: {
+            ALOGD("[validatePictureParameter] validate saturation value");
+            int value = param.get<PictureParameter::Tag::saturation>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::hue: {
+            ALOGD("[validatePictureParameter] validate hue value");
+            int value = param.get<PictureParameter::Tag::hue>();
+            EXPECT_GE(value, -50);
+            EXPECT_LE(value, 50);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerBrightness: {
+            ALOGD("[validatePictureParameter] validate colorTunerBrightness value");
+            int value = param.get<PictureParameter::Tag::colorTunerBrightness>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerSaturation: {
+            ALOGD("[validatePictureParameter] validate colorTunerSaturation value");
+            int value = param.get<PictureParameter::Tag::colorTunerSaturation>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerHue: {
+            ALOGD("[validatePictureParameter] validate colorTunerHue value");
+            int value = param.get<PictureParameter::Tag::colorTunerHue>();
+            EXPECT_GE(value, -50);
+            EXPECT_LE(value, 50);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerRedOffset: {
+            ALOGD("[validatePictureParameter] validate colorTunerRedOffset value");
+            int value = param.get<PictureParameter::Tag::colorTunerRedOffset>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerGreenOffset: {
+            ALOGD("[validatePictureParameter] validate colorTunerGreenOffset value");
+            int value = param.get<PictureParameter::Tag::colorTunerGreenOffset>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerBlueOffset: {
+            ALOGD("[validatePictureParameter] validate colorTunerBlueOffset value");
+            int value = param.get<PictureParameter::Tag::colorTunerBlueOffset>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerRedGain: {
+            ALOGD("[validatePictureParameter] validate colorTunerRedGain value");
+            int value = param.get<PictureParameter::Tag::colorTunerRedGain>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerGreenGain: {
+            ALOGD("[validatePictureParameter] validate colorTunerGreenGain value");
+            int value = param.get<PictureParameter::Tag::colorTunerGreenGain>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerBlueGain: {
+            ALOGD("[validatePictureParameter] validate colorTunerBlueGain value");
+            int value = param.get<PictureParameter::Tag::colorTunerBlueGain>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::panelInitMaxLuminceNits: {
+            ALOGD("[validatePictureParameter] validate panelInitMaxLuminceNits value");
+            int value = param.get<PictureParameter::Tag::panelInitMaxLuminceNits>();
+            EXPECT_GE(value, 0);
+            EXPECT_LE(value, 10000);
+            break;
+        }
+        case PictureParameter::Tag::colorTemperatureRedGain: {
+            ALOGD("[validatePictureParameter] validate colorTemperatureRedGain value");
+            int value = param.get<PictureParameter::Tag::colorTemperatureRedGain>();
+            validateColorTemperature(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTemperatureGreenGain: {
+            ALOGD("[validatePictureParameter] validate colorTemperatureGreenGain value");
+            int value = param.get<PictureParameter::Tag::colorTemperatureGreenGain>();
+            validateColorTemperature(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTemperatureBlueGain: {
+            ALOGD("[validatePictureParameter] validate colorTemperatureBlueGain value");
+            int value = param.get<PictureParameter::Tag::colorTemperatureBlueGain>();
+            validateColorTemperature(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTemperatureRedOffset: {
+            ALOGD("[validatePictureParameter] validate ccolorTemperatureRedOffset value");
+            int value = param.get<PictureParameter::Tag::colorTemperatureRedOffset>();
+            validateColorTemperature(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTemperatureGreenOffset: {
+            ALOGD("[validatePictureParameter] validate colorTemperatureGreenOffset value");
+            int value = param.get<PictureParameter::Tag::colorTemperatureGreenOffset>();
+            validateColorTemperature(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTemperatureBlueOffset: {
+            ALOGD("[validatePictureParameter] validate colorTemperatureBlueOffset value");
+            int value = param.get<PictureParameter::Tag::colorTemperatureBlueOffset>();
+            validateColorTemperature(value);
+            break;
+        }
+        case PictureParameter::Tag::elevenPointRed: {
+            ALOGD("[validatePictureParameter] validate elevenPointRed value");
+            std::array<int, 11> elevenPointValues =
+                    param.get<PictureParameter::Tag::elevenPointRed>();
+            for (int value : elevenPointValues) {
+                validateParameterRange0To100(value);
+            }
+            break;
+        }
+        case PictureParameter::Tag::elevenPointGreen: {
+            ALOGD("[validatePictureParameter] validate elevenPointGreen value");
+            std::array<int, 11> elevenPointValues =
+                    param.get<PictureParameter::Tag::elevenPointGreen>();
+            for (int value : elevenPointValues) {
+                validateParameterRange0To100(value);
+            }
+            break;
+        }
+        case PictureParameter::Tag::elevenPointBlue: {
+            ALOGD("[validatePictureParameter] validate elevenPointBlue value");
+            std::array<int, 11> elevenPointValues =
+                    param.get<PictureParameter::Tag::elevenPointBlue>();
+            for (int value : elevenPointValues) {
+                validateParameterRange0To100(value);
+            }
+            break;
+        }
+        case PictureParameter::Tag::osdRedGain: {
+            ALOGD("[validatePictureParameter] validate osdRedGain value");
+            int value = param.get<PictureParameter::Tag::osdRedGain>();
+            validateParameterRange0To2047(value);
+            break;
+        }
+        case PictureParameter::Tag::osdGreenGain: {
+            ALOGD("[validatePictureParameter] validate osdGreenGain value");
+            int value = param.get<PictureParameter::Tag::osdGreenGain>();
+            validateParameterRange0To2047(value);
+            break;
+        }
+        case PictureParameter::Tag::osdBlueGain: {
+            ALOGD("[validatePictureParameter] validate osdBlueGain value");
+            int value = param.get<PictureParameter::Tag::osdBlueGain>();
+            validateParameterRange0To2047(value);
+            break;
+        }
+        case PictureParameter::Tag::osdRedOffset: {
+            ALOGD("[validatePictureParameter] validate osdRedOffset value");
+            int value = param.get<PictureParameter::Tag::osdRedOffset>();
+            validateParameterRange0To2047(value);
+            break;
+        }
+        case PictureParameter::Tag::osdGreenOffset: {
+            ALOGD("[validatePictureParameter] validate osdGreenOffset value");
+            int value = param.get<PictureParameter::Tag::osdGreenOffset>();
+            validateParameterRange0To2047(value);
+            break;
+        }
+        case PictureParameter::Tag::osdBlueOffset: {
+            ALOGD("[validatePictureParameter] validate osdBlueOffset value");
+            int value = param.get<PictureParameter::Tag::osdBlueOffset>();
+            validateParameterRange0To2047(value);
+            break;
+        }
+        case PictureParameter::Tag::osdHue: {
+            ALOGD("[validatePictureParameter] validate osdHue value");
+            int value = param.get<PictureParameter::Tag::osdHue>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::osdSaturation: {
+            ALOGD("[validatePictureParameter] validate osdSaturation value");
+            int value = param.get<PictureParameter::Tag::osdSaturation>();
+            EXPECT_GE(value, 0);
+            EXPECT_LE(value, 255);
+            break;
+        }
+        case PictureParameter::Tag::osdContrast: {
+            ALOGD("[validatePictureParameter] validate osdContrast value");
+            int value = param.get<PictureParameter::Tag::osdContrast>();
+            validateParameterRange0To2047(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerHueRed: {
+            ALOGD("[validatePictureParameter] validate colorTunerHueRed value");
+            int value = param.get<PictureParameter::Tag::colorTunerHueRed>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerHueGreen: {
+            ALOGD("[validatePictureParameter] validate colorTunerHueGreen value");
+            int value = param.get<PictureParameter::Tag::colorTunerHueGreen>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerHueBlue: {
+            ALOGD("[validatePictureParameter] validate colorTunerHueBlue value");
+            int value = param.get<PictureParameter::Tag::colorTunerHueBlue>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerHueCyan: {
+            ALOGD("[validatePictureParameter] validate colorTunerHueCyan value");
+            int value = param.get<PictureParameter::Tag::colorTunerHueCyan>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerHueMagenta: {
+            ALOGD("[validatePictureParameter] validate colorTunerHueMagenta value");
+            int value = param.get<PictureParameter::Tag::colorTunerHueMagenta>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerHueYellow: {
+            ALOGD("[validatePictureParameter] validate colorTunerHueYellow value");
+            int value = param.get<PictureParameter::Tag::colorTunerHueYellow>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerHueFlesh: {
+            ALOGD("[validatePictureParameter] validate colorTunerHueFlesh value");
+            int value = param.get<PictureParameter::Tag::colorTunerHueFlesh>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerSaturationRed: {
+            ALOGD("[validatePictureParameter] validate colorTunerSaturationRed value");
+            int value = param.get<PictureParameter::Tag::colorTunerSaturationRed>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerSaturationGreen: {
+            ALOGD("[validatePictureParameter] validate colorTunerSaturationGreen value");
+            int value = param.get<PictureParameter::Tag::colorTunerSaturationGreen>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerSaturationBlue: {
+            ALOGD("[validatePictureParameter] validate colorTunerSaturationBlue value");
+            int value = param.get<PictureParameter::Tag::colorTunerSaturationBlue>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerSaturationCyan: {
+            ALOGD("[validatePictureParameter] validate colorTunerSaturationCyan value");
+            int value = param.get<PictureParameter::Tag::colorTunerSaturationCyan>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerSaturationMagenta: {
+            ALOGD("[validatePictureParameter] validate colorTunerSaturationMagenta value");
+            int value = param.get<PictureParameter::Tag::colorTunerSaturationMagenta>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerSaturationYellow: {
+            ALOGD("[validatePictureParameter] validate colorTunerSaturationYellow value");
+            int value = param.get<PictureParameter::Tag::colorTunerSaturationYellow>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerSaturationFlesh: {
+            ALOGD("[validatePictureParameter] validate colorTunerSaturationFlesh value");
+            int value = param.get<PictureParameter::Tag::colorTunerSaturationFlesh>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerLuminanceRed: {
+            ALOGD("[validatePictureParameter] validate colorTunerLuminanceRed value");
+            int value = param.get<PictureParameter::Tag::colorTunerLuminanceRed>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerLuminanceGreen: {
+            ALOGD("[validatePictureParameter] validate colorTunerLuminanceGreen value");
+            int value = param.get<PictureParameter::Tag::colorTunerLuminanceGreen>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerLuminanceBlue: {
+            ALOGD("[validatePictureParameter] validate colorTunerLuminanceBlue value");
+            int value = param.get<PictureParameter::Tag::colorTunerLuminanceBlue>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerLuminanceCyan: {
+            ALOGD("[validatePictureParameter] validate colorTunerLuminanceCyan value");
+            int value = param.get<PictureParameter::Tag::colorTunerLuminanceCyan>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerLuminanceMagenta: {
+            ALOGD("[validatePictureParameter] validate colorTunerLuminanceMagenta value");
+            int value = param.get<PictureParameter::Tag::colorTunerLuminanceMagenta>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerLuminanceYellow: {
+            ALOGD("[validatePictureParameter] validate colorTunerLuminanceYellow value");
+            int value = param.get<PictureParameter::Tag::colorTunerLuminanceYellow>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case PictureParameter::Tag::colorTunerLuminanceFlesh: {
+            ALOGD("[validatePictureParameter] validate colorTunerLuminanceFlesh value");
+            int value = param.get<PictureParameter::Tag::colorTunerLuminanceFlesh>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        default:
+            ALOGD("Those parameters don't need to check.");
+            break;
+    }
+}
+
+void validateSoundParameter(const SoundParameter& param) {
+    switch (param.getTag()) {
+        case SoundParameter::Tag::balance: {
+            ALOGD("[validateSoundParameter] validate balance value");
+            int value = param.get<SoundParameter::Tag::balance>();
+            EXPECT_GE(value, -50);
+            EXPECT_LE(value, 50);
+            break;
+        }
+        case SoundParameter::Tag::bass: {
+            ALOGD("[validateSoundParameter] validate bass value");
+            int value = param.get<SoundParameter::Tag::bass>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case SoundParameter::Tag::treble: {
+            ALOGD("[validateSoundParameter] validate treble value");
+            int value = param.get<SoundParameter::Tag::treble>();
+            validateParameterRange0To100(value);
+            break;
+        }
+        case SoundParameter::Tag::speakersDelayMs: {
+            ALOGD("[validateSoundParameter] validate speakersDelayMs value");
+            int value = param.get<SoundParameter::Tag::speakersDelayMs>();
+            EXPECT_GE(value, 0);
+            EXPECT_LE(value, 250);
+            break;
+        }
+        case SoundParameter::Tag::digitalOutputDelayMs: {
+            ALOGD("[validateSoundParameter] validate digitalOutputDelayMs value");
+            int value = param.get<SoundParameter::Tag::digitalOutputDelayMs>();
+            EXPECT_GE(value, 0);
+            EXPECT_LE(value, 250);
+            break;
+        }
+        default:
+            ALOGD("Those parameters don't need to check.");
+            break;
+    }
+}
+
 class MediaQualityCallback : public BnMediaQualityCallback {
   public:
     explicit MediaQualityCallback(
@@ -81,7 +485,11 @@
             const std::function<void(const PictureProfile& pictureProfile)>&
                     on_hal_picture_profile_adjust)
         : on_hal_picture_profile_adjust_(on_hal_picture_profile_adjust) {}
+
     ScopedAStatus onPictureProfileAdjusted(const PictureProfile& pictureProfile) override {
+        for (const auto& param : pictureProfile.parameters.pictureParameters) {
+            validatePictureParameter(param);
+        }
         on_hal_picture_profile_adjust_(pictureProfile);
         return ScopedAStatus::ok();
     }
@@ -95,7 +503,9 @@
         return ScopedAStatus::ok();
     }
 
-    ScopedAStatus onRequestPictureParameters(int64_t) { return ScopedAStatus::ok(); }
+    ScopedAStatus requestPictureParameters(int64_t) { return ScopedAStatus::ok(); }
+
+    ScopedAStatus onStreamStatusChanged(int64_t, StreamStatus) { return ScopedAStatus::ok(); }
 
   private:
     std::function<void(const PictureProfile& pictureProfile)> on_hal_picture_profile_adjust_;
@@ -107,7 +517,11 @@
             const std::function<void(const SoundProfile& soundProfile)>&
                     on_hal_sound_profile_adjust)
         : on_hal_sound_profile_adjust_(on_hal_sound_profile_adjust) {}
+
     ScopedAStatus onSoundProfileAdjusted(const SoundProfile& soundProfile) override {
+        for (const auto& param : soundProfile.parameters.soundParameters) {
+            validateSoundParameter(param);
+        }
         on_hal_sound_profile_adjust_(soundProfile);
         return ScopedAStatus::ok();
     }
@@ -121,7 +535,7 @@
         return ScopedAStatus::ok();
     }
 
-    ScopedAStatus onRequestSoundParameters(int64_t) { return ScopedAStatus::ok(); }
+    ScopedAStatus requestSoundParameters(int64_t) { return ScopedAStatus::ok(); }
 
   private:
     std::function<void(const SoundProfile& soundProfile)> on_hal_sound_profile_adjust_;
@@ -147,7 +561,7 @@
                 open_cb_promise.set_value();
                 return ScopedAStatus::ok();
             });
-    ASSERT_OK(mediaquality->setCallback(callback));
+    ASSERT_OK(mediaquality->setAmbientBacklightCallback(callback));
     ASSERT_OK(mediaquality->setAmbientBacklightDetectionEnabled(true));
     std::chrono::milliseconds timeout{10000};
     EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
@@ -161,7 +575,7 @@
 TEST_P(MediaQualityAidl, TestSetMediaQualityCallback) {
     std::shared_ptr<MediaQualityCallback> callback = ndk::SharedRefBase::make<MediaQualityCallback>(
             [](auto /* event */) { return ScopedAStatus::ok(); });
-    ASSERT_OK(mediaquality->setCallback(callback));
+    ASSERT_OK(mediaquality->setAmbientBacklightCallback(callback));
 }
 
 TEST_P(MediaQualityAidl, TestSetPictureProfileAdjustmentListener) {
@@ -212,13 +626,13 @@
 
 TEST_P(MediaQualityAidl, TestSetAmbientBacklightDetector) {
     AmbientBacklightSettings in_settings = {
-            .packageName = "com.android.mediaquality",
+            .uid = 1,
             .source = AmbientBacklightSource::VIDEO,
             .colorFormat = PixelFormat::RGB_888,
             .hZonesNumber = 32,
             .vZonesNumber = 20,
             .hasLetterbox = true,
-            .threshold = 0,
+            .colorThreshold = 0,
     };
     ASSERT_OK(mediaquality->setAmbientBacklightDetector(in_settings));
 }
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
index 2b39bc6..158e4f1 100644
--- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
@@ -100,7 +100,9 @@
     ASSERT_TRUE(mFilterTests.configFilter(filterReconf.settings, filterId));
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
     ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
-    ASSERT_TRUE(mFilterTests.startIdTest(filterId));
+    if (!isPassthroughFilter(filterReconf)) {
+        ASSERT_TRUE(mFilterTests.startIdTest(filterId));
+    }
     ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
@@ -152,7 +154,9 @@
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
     // tune test
     ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
-    ASSERT_TRUE(filterDataOutputTest());
+    if (!isPassthroughFilter(filterConf)) {
+        ASSERT_TRUE(filterDataOutputTest());
+    }
     ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
@@ -210,7 +214,9 @@
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
     // tune test
     ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
-    ASSERT_TRUE(filterDataOutputTest());
+    if (!isPassthroughFilter(filterConf)) {
+        ASSERT_TRUE(filterDataOutputTest());
+    }
     ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
     ASSERT_TRUE(mFilterTests.releaseShareAvHandle(filterId));
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h
index be9b996..5fdc3dc 100644
--- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h
@@ -89,6 +89,28 @@
     sectionFilterIds.clear();
 }
 
+bool isPassthroughFilter(FilterConfig filterConfig) {
+    auto type = filterConfig.type;
+    if (type.mainType == DemuxFilterMainType::TS) {
+        auto subType = type.subType.get<DemuxFilterSubType::Tag::tsFilterType>();
+        if (subType == DemuxTsFilterType::AUDIO || subType == DemuxTsFilterType::VIDEO) {
+            auto tsFilterSettings = filterConfig.settings.get<DemuxFilterSettings::Tag::ts>();
+            auto avSettings = tsFilterSettings.filterSettings
+                    .get<DemuxTsFilterSettingsFilterSettings::Tag::av>();
+            return avSettings.isPassthrough;
+        }
+    } else if (filterConfig.type.mainType != DemuxFilterMainType::MMTP) {
+        auto subType = type.subType.get<DemuxFilterSubType::Tag::mmtpFilterType>();
+        if (subType == DemuxMmtpFilterType::AUDIO || subType == DemuxMmtpFilterType::VIDEO) {
+            auto mmtpFilterSettings = filterConfig.settings.get<DemuxFilterSettings::Tag::mmtp>();
+            auto avSettings = mmtpFilterSettings.filterSettings
+                    .get<DemuxMmtpFilterSettingsFilterSettings::Tag::av>();
+            return avSettings.isPassthrough;
+        }
+    }
+    return false;
+}
+
 enum class Dataflow_Context { LNBRECORD, RECORD, DESCRAMBLING, LNBDESCRAMBLING };
 
 class TunerLnbAidlTest : public testing::TestWithParam<std::string> {
diff --git a/wifi/aidl/default/aidl_struct_util.cpp b/wifi/aidl/default/aidl_struct_util.cpp
index 75e9bfe..6de150e 100644
--- a/wifi/aidl/default/aidl_struct_util.cpp
+++ b/wifi/aidl/default/aidl_struct_util.cpp
@@ -121,7 +121,8 @@
                                       WIFI_FEATURE_SET_LATENCY_MODE,
                                       WIFI_FEATURE_P2P_RAND_MAC,
                                       WIFI_FEATURE_AFC_CHANNEL,
-                                      WIFI_FEATURE_SET_VOIP_MODE};
+                                      WIFI_FEATURE_SET_VOIP_MODE,
+                                      WIFI_FEATURE_MLO_SAP};
     for (const auto feature : features) {
         if (feature & legacy_feature_set) {
             *aidl_feature_set |= static_cast<uint32_t>(convertLegacyChipFeatureToAidl(feature));
@@ -1862,9 +1863,7 @@
     legacy_request->ranging_auto_response = aidl_request.baseConfigs.rangingRequired
                                                     ? legacy_hal::NAN_RANGING_AUTO_RESPONSE_ENABLE
                                                     : legacy_hal::NAN_RANGING_AUTO_RESPONSE_DISABLE;
-    legacy_request->sdea_params.range_report = aidl_request.rangingResultsRequired
-                                                       ? legacy_hal::NAN_ENABLE_RANGE_REPORT
-                                                       : legacy_hal::NAN_DISABLE_RANGE_REPORT;
+    legacy_request->sdea_params.range_report = legacy_hal::NAN_DISABLE_RANGE_REPORT;
     legacy_request->publish_type = convertAidlNanPublishTypeToLegacy(aidl_request.publishType);
     legacy_request->tx_type = convertAidlNanTxTypeToLegacy(aidl_request.txType);
     legacy_request->service_responder_policy = aidl_request.autoAcceptDataPathRequests
@@ -1993,17 +1992,6 @@
     legacy_request->ranging_cfg.distance_ingress_mm =
             aidl_request.baseConfigs.distanceIngressCm * 10;
     legacy_request->ranging_cfg.distance_egress_mm = aidl_request.baseConfigs.distanceEgressCm * 10;
-    legacy_request->ranging_cfg.rtt_burst_size = aidl_request.baseConfigs.rttBurstSize;
-    legacy_request->ranging_cfg.preamble =
-            convertAidlRttPreambleToLegacy(aidl_request.baseConfigs.preamble);
-    if (aidl_request.baseConfigs.channelInfo.has_value()) {
-        if (!convertAidlWifiChannelInfoToLegacy(aidl_request.baseConfigs.channelInfo.value(),
-                                                &legacy_request->ranging_cfg.channel_info)) {
-            LOG(ERROR) << "convertAidlNanSubscribeRequestToLegacy: "
-                          "Unable to convert aidl channel info to legacy";
-            return false;
-        }
-    }
     legacy_request->ranging_auto_response = aidl_request.baseConfigs.rangingRequired
                                                     ? legacy_hal::NAN_RANGING_AUTO_RESPONSE_ENABLE
                                                     : legacy_hal::NAN_RANGING_AUTO_RESPONSE_DISABLE;
@@ -2312,9 +2300,10 @@
     aidl_response->supportsPairing = legacy_response.is_pairing_supported;
     aidl_response->supportsSetClusterId = legacy_response.is_set_cluster_id_supported;
     aidl_response->supportsSuspension = legacy_response.is_suspension_supported;
-    aidl_response->supportsPeriodicRanging = legacy_response.is_periodic_ranging_supported;
-    aidl_response->maxSupportedBandwidth = convertLegacyRttBwToAidl(legacy_response.supported_bw);
-    aidl_response->maxNumRxChainsSupported = legacy_response.num_rx_chains_supported;
+    // TODO: Retrieve values from the legacy HAL
+    aidl_response->supportsPeriodicRanging = false;
+    aidl_response->maxSupportedBandwidth = RttBw::BW_UNSPECIFIED;
+    aidl_response->maxNumRxChainsSupported = 2;
 
     return true;
 }
@@ -2826,6 +2815,138 @@
     return true;
 }
 
+long convertLegacyAkmsToAidl(legacy_hal::wifi_rtt_akm akms) {
+    long aidl_akms = Akm::NONE;
+    if ((akms & legacy_hal::WPA_KEY_MGMT_PASN) != 0) {
+        aidl_akms |= Akm::PASN;
+    }
+    if ((akms & legacy_hal::WPA_KEY_MGMT_SAE) != 0) {
+        aidl_akms |= Akm::SAE;
+    }
+    if ((akms & legacy_hal::WPA_KEY_MGMT_EAP_FT_SHA256) != 0) {
+        aidl_akms |= Akm::FT_EAP_SHA256;
+    }
+    if ((akms & legacy_hal::WPA_KEY_MGMT_FT_PSK_SHA256) != 0) {
+        aidl_akms |= Akm::FT_PSK_SHA256;
+    }
+    if ((akms & legacy_hal::WPA_KEY_MGMT_EAP_FT_SHA384) != 0) {
+        aidl_akms |= Akm::FT_EAP_SHA384;
+    }
+    if ((akms & legacy_hal::WPA_KEY_MGMT_FT_PSK_SHA384) != 0) {
+        aidl_akms |= Akm::FT_PSK_SHA384;
+    }
+    if ((akms & legacy_hal::WPA_KEY_MGMT_EAP_FILS_SHA256) != 0) {
+        aidl_akms |= Akm::FILS_EAP_SHA256;
+    }
+    if ((akms & legacy_hal::WPA_KEY_MGMT_EAP_FILS_SHA384) != 0) {
+        aidl_akms |= Akm::FILS_EAP_SHA384;
+    }
+    return aidl_akms;
+}
+
+legacy_hal::wifi_rtt_akm convertAidlAkmToLegacy(long akm) {
+    switch (akm) {
+        case Akm::PASN:
+            return legacy_hal::WPA_KEY_MGMT_PASN;
+        case Akm::SAE:
+            return legacy_hal::WPA_KEY_MGMT_SAE;
+        case Akm::FT_EAP_SHA256:
+            return legacy_hal::WPA_KEY_MGMT_EAP_FT_SHA256;
+        case Akm::FT_PSK_SHA256:
+            return legacy_hal::WPA_KEY_MGMT_FT_PSK_SHA256;
+        case Akm::FT_EAP_SHA384:
+            return legacy_hal::WPA_KEY_MGMT_EAP_FT_SHA384;
+        case Akm::FT_PSK_SHA384:
+            return legacy_hal::WPA_KEY_MGMT_FT_PSK_SHA384;
+        case Akm::FILS_EAP_SHA256:
+            return legacy_hal::WPA_KEY_MGMT_EAP_FILS_SHA256;
+        case Akm::FILS_EAP_SHA384:
+            return legacy_hal::WPA_KEY_MGMT_EAP_FILS_SHA384;
+        default:
+            return legacy_hal::WPA_KEY_MGMT_NONE;
+    }
+}
+
+long convertLegacyCipherSuitesToAidl(legacy_hal::wifi_rtt_cipher_suite ciphers) {
+    long aidl_ciphers = CipherSuite::NONE;
+    if ((ciphers & legacy_hal::WPA_CIPHER_CCMP_128) != 0) {
+        aidl_ciphers |= CipherSuite::CCMP_128;
+    }
+    if ((ciphers & legacy_hal::WPA_CIPHER_CCMP_256) != 0) {
+        aidl_ciphers |= CipherSuite::CCMP_256;
+    }
+    if ((ciphers & legacy_hal::WPA_CIPHER_GCMP_128) != 0) {
+        aidl_ciphers |= CipherSuite::GCMP_128;
+    }
+    if ((ciphers & legacy_hal::WPA_CIPHER_GCMP_256) != 0) {
+        aidl_ciphers |= CipherSuite::GCMP_256;
+    }
+    return aidl_ciphers;
+}
+
+legacy_hal::wifi_rtt_cipher_suite convertAidlCipherSuiteToLegacy(long cipher) {
+    switch (cipher) {
+        case CipherSuite::CCMP_128:
+            return WPA_CIPHER_CCMP_128;
+        case CipherSuite::CCMP_256:
+            return WPA_CIPHER_CCMP_256;
+        case CipherSuite::GCMP_128:
+            return WPA_CIPHER_GCMP_128;
+        case CipherSuite::GCMP_256:
+            return WPA_CIPHER_GCMP_256;
+        default:
+            return WPA_CIPHER_NONE;
+    }
+}
+
+bool convertAidlRttConfigToLegacyV4(const RttConfig& aidl_config,
+                                    legacy_hal::wifi_rtt_config_v4* legacy_config) {
+    if (!legacy_config) {
+        return false;
+    }
+    *legacy_config = {};
+    if (!convertAidlRttConfigToLegacyV3(aidl_config, &(legacy_config->rtt_config))) {
+        return false;
+    }
+    if (aidl_config.secureConfig.has_value()) {
+        legacy_config->rtt_secure_config.enable_secure_he_ltf =
+                aidl_config.secureConfig->enableSecureHeLtf;
+        legacy_config->rtt_secure_config.enable_ranging_frame_protection =
+                aidl_config.secureConfig->enableRangingFrameProtection;
+        if (aidl_config.secureConfig->pasnComebackCookie.has_value() &&
+            aidl_config.secureConfig->pasnComebackCookie->size() <= RTT_MAX_COOKIE_LEN) {
+            legacy_config->rtt_secure_config.pasn_config.comeback_cookie_len =
+                    aidl_config.secureConfig->pasnComebackCookie->size();
+            memcpy(legacy_config->rtt_secure_config.pasn_config.comeback_cookie,
+                   aidl_config.secureConfig->pasnComebackCookie->data(),
+                   aidl_config.secureConfig->pasnComebackCookie->size());
+        }
+        legacy_config->rtt_secure_config.pasn_config.base_akm =
+                convertAidlAkmToLegacy(aidl_config.secureConfig->pasnConfig.baseAkm);
+        legacy_config->rtt_secure_config.pasn_config.pairwise_cipher_suite =
+                convertAidlCipherSuiteToLegacy(aidl_config.secureConfig->pasnConfig.cipherSuite);
+        if (aidl_config.secureConfig->pasnConfig.passphrase.has_value() &&
+            aidl_config.secureConfig->pasnConfig.passphrase->size() <=
+                    RTT_SECURITY_MAX_PASSPHRASE_LEN) {
+            legacy_config->rtt_secure_config.pasn_config.passphrase_len =
+                    aidl_config.secureConfig->pasnConfig.passphrase->size();
+            memcpy(legacy_config->rtt_secure_config.pasn_config.passphrase,
+                   aidl_config.secureConfig->pasnConfig.passphrase->data(),
+                   aidl_config.secureConfig->pasnConfig.passphrase->size());
+        }
+        if (aidl_config.secureConfig->pasnConfig.pmkid.has_value() &&
+            aidl_config.secureConfig->pasnConfig.pmkid->size() == PMKID_LEN) {
+            legacy_config->rtt_secure_config.pasn_config.pmkid_len =
+                    aidl_config.secureConfig->pasnConfig.pmkid->size();
+            memcpy(legacy_config->rtt_secure_config.pasn_config.pmkid,
+                   aidl_config.secureConfig->pasnConfig.pmkid->data(),
+                   aidl_config.secureConfig->pasnConfig.pmkid->size());
+        }
+    }
+
+    return true;
+}
+
 bool convertAidlVectorOfRttConfigToLegacy(
         const std::vector<RttConfig>& aidl_configs,
         std::vector<legacy_hal::wifi_rtt_config>* legacy_configs) {
@@ -2860,6 +2981,23 @@
     return true;
 }
 
+bool convertAidlVectorOfRttConfigToLegacyV4(
+        const std::vector<RttConfig>& aidl_configs,
+        std::vector<legacy_hal::wifi_rtt_config_v4>* legacy_configs) {
+    if (!legacy_configs) {
+        return false;
+    }
+    *legacy_configs = {};
+    for (const auto& aidl_config : aidl_configs) {
+        legacy_hal::wifi_rtt_config_v4 legacy_config;
+        if (!convertAidlRttConfigToLegacyV4(aidl_config, &legacy_config)) {
+            return false;
+        }
+        legacy_configs->push_back(legacy_config);
+    }
+    return true;
+}
+
 bool convertAidlRttLciInformationToLegacy(const RttLciInformation& aidl_info,
                                           legacy_hal::wifi_lci_information* legacy_info) {
     if (!legacy_info) {
@@ -2970,6 +3108,12 @@
     aidl_capabilities->azBwSupport = (int)RttBw::BW_UNSPECIFIED;
     aidl_capabilities->ntbInitiatorSupported = false;
     aidl_capabilities->ntbResponderSupported = false;
+    // Initialize 11az secure ranging parameters to default
+    aidl_capabilities->akmsSupported = Akm::NONE;
+    aidl_capabilities->cipherSuitesSupported = CipherSuite::NONE;
+    aidl_capabilities->secureHeLtfSupported = false;
+    aidl_capabilities->rangingFrameProtectionSupported = false;
+    aidl_capabilities->maxSupportedSecureHeLtfProtocolVersion = false;
     return true;
 }
 
@@ -2997,6 +3141,53 @@
             (int)convertLegacyRttBwBitmapToAidl(legacy_capabilities_v3.az_bw_support);
     aidl_capabilities->ntbInitiatorSupported = legacy_capabilities_v3.ntb_initiator_supported;
     aidl_capabilities->ntbResponderSupported = legacy_capabilities_v3.ntb_responder_supported;
+    // Initialize 11az secure ranging parameters to default
+    aidl_capabilities->akmsSupported = Akm::NONE;
+    aidl_capabilities->cipherSuitesSupported = CipherSuite::NONE;
+    aidl_capabilities->secureHeLtfSupported = false;
+    aidl_capabilities->rangingFrameProtectionSupported = false;
+    aidl_capabilities->maxSupportedSecureHeLtfProtocolVersion = false;
+
+    return true;
+}
+
+bool convertLegacyRttCapabilitiesV4ToAidl(
+        const legacy_hal::wifi_rtt_capabilities_v4& legacy_capabilities_v4,
+        RttCapabilities* aidl_capabilities) {
+    if (!aidl_capabilities) {
+        return false;
+    }
+    *aidl_capabilities = {};
+    aidl_capabilities->rttOneSidedSupported =
+            legacy_capabilities_v4.rtt_capab_v3.rtt_capab.rtt_one_sided_supported;
+    aidl_capabilities->rttFtmSupported =
+            legacy_capabilities_v4.rtt_capab_v3.rtt_capab.rtt_ftm_supported;
+    aidl_capabilities->lciSupported = legacy_capabilities_v4.rtt_capab_v3.rtt_capab.lci_support;
+    aidl_capabilities->lcrSupported = legacy_capabilities_v4.rtt_capab_v3.rtt_capab.lcr_support;
+    aidl_capabilities->responderSupported =
+            legacy_capabilities_v4.rtt_capab_v3.rtt_capab.responder_supported;
+    aidl_capabilities->preambleSupport = convertLegacyRttPreambleBitmapToAidl(
+            legacy_capabilities_v4.rtt_capab_v3.rtt_capab.preamble_support);
+    aidl_capabilities->bwSupport = convertLegacyRttBwBitmapToAidl(
+            legacy_capabilities_v4.rtt_capab_v3.rtt_capab.bw_support);
+    aidl_capabilities->mcVersion = legacy_capabilities_v4.rtt_capab_v3.rtt_capab.mc_version;
+    aidl_capabilities->azPreambleSupport = (int)convertLegacyRttPreambleBitmapToAidl(
+            legacy_capabilities_v4.rtt_capab_v3.az_preamble_support);
+    aidl_capabilities->azBwSupport =
+            (int)convertLegacyRttBwBitmapToAidl(legacy_capabilities_v4.rtt_capab_v3.az_bw_support);
+    aidl_capabilities->ntbInitiatorSupported =
+            legacy_capabilities_v4.rtt_capab_v3.ntb_initiator_supported;
+    aidl_capabilities->ntbResponderSupported =
+            legacy_capabilities_v4.rtt_capab_v3.ntb_responder_supported;
+    aidl_capabilities->akmsSupported =
+            convertLegacyAkmsToAidl(legacy_capabilities_v4.supported_akms);
+    aidl_capabilities->cipherSuitesSupported =
+            convertLegacyCipherSuitesToAidl(legacy_capabilities_v4.supported_cipher_suites);
+    aidl_capabilities->secureHeLtfSupported = legacy_capabilities_v4.secure_he_ltf_supported;
+    aidl_capabilities->rangingFrameProtectionSupported =
+            legacy_capabilities_v4.ranging_fame_protection_supported;
+    aidl_capabilities->maxSupportedSecureHeLtfProtocolVersion =
+            legacy_capabilities_v4.max_supported_secure_he_ltf_protocol_ver;
     return true;
 }
 
@@ -3077,6 +3268,13 @@
         aidl_result.ntbMaxMeasurementTime = 0;
         aidl_result.numTxSpatialStreams = 0;
         aidl_result.numRxSpatialStreams = 0;
+        aidl_result.isRangingFrameProtectionEnabled = false;
+        aidl_result.isSecureLtfEnabled = false;
+        aidl_result.baseAkm = Akm::NONE;
+        aidl_result.cipherSuite = CipherSuite::NONE;
+        aidl_result.secureHeLtfProtocolVersion = 0;
+        aidl_result.pasnComebackAfterMillis = 0;
+        aidl_result.pasnComebackCookie = std::nullopt;
         aidl_results->push_back(aidl_result);
     }
     return true;
@@ -3103,6 +3301,13 @@
         aidl_result.ntbMaxMeasurementTime = 0;
         aidl_result.numTxSpatialStreams = 0;
         aidl_result.numRxSpatialStreams = 0;
+        aidl_result.isRangingFrameProtectionEnabled = false;
+        aidl_result.isSecureLtfEnabled = false;
+        aidl_result.baseAkm = Akm::NONE;
+        aidl_result.cipherSuite = CipherSuite::NONE;
+        aidl_result.secureHeLtfProtocolVersion = 0;
+        aidl_result.pasnComebackAfterMillis = 0;
+        aidl_result.pasnComebackCookie = std::nullopt;
         aidl_results->push_back(aidl_result);
     }
     return true;
@@ -3130,6 +3335,57 @@
         aidl_result.ntbMaxMeasurementTime = legacy_result->ntb_max_measurement_time;
         aidl_result.numTxSpatialStreams = legacy_result->num_tx_sts;
         aidl_result.numRxSpatialStreams = legacy_result->num_rx_sts;
+        aidl_result.isRangingFrameProtectionEnabled = false;
+        aidl_result.isSecureLtfEnabled = false;
+        aidl_result.baseAkm = Akm::NONE;
+        aidl_result.cipherSuite = CipherSuite::NONE;
+        aidl_result.secureHeLtfProtocolVersion = 0;
+        aidl_result.pasnComebackAfterMillis = 0;
+        aidl_result.pasnComebackCookie = std::nullopt;
+        aidl_results->push_back(aidl_result);
+    }
+    return true;
+}
+
+bool convertLegacyVectorOfRttResultV4ToAidl(
+        const std::vector<const legacy_hal::wifi_rtt_result_v4*>& legacy_results,
+        std::vector<RttResult>* aidl_results) {
+    if (!aidl_results) {
+        return false;
+    }
+    *aidl_results = {};
+    for (const auto legacy_result : legacy_results) {
+        RttResult aidl_result;
+        if (!convertLegacyRttResultToAidl(legacy_result->rtt_result_v3.rtt_result.rtt_result,
+                                          &aidl_result)) {
+            return false;
+        }
+        aidl_result.channelFreqMHz =
+                legacy_result->rtt_result_v3.rtt_result.frequency != UNSPECIFIED
+                        ? legacy_result->rtt_result_v3.rtt_result.frequency
+                        : 0;
+        aidl_result.packetBw =
+                convertLegacyRttBwToAidl(legacy_result->rtt_result_v3.rtt_result.packet_bw);
+        aidl_result.i2rTxLtfRepetitionCount =
+                legacy_result->rtt_result_v3.i2r_tx_ltf_repetition_count;
+        aidl_result.r2iTxLtfRepetitionCount =
+                legacy_result->rtt_result_v3.r2i_tx_ltf_repetition_count;
+        aidl_result.ntbMinMeasurementTime = legacy_result->rtt_result_v3.ntb_min_measurement_time;
+        aidl_result.ntbMaxMeasurementTime = legacy_result->rtt_result_v3.ntb_max_measurement_time;
+        aidl_result.numTxSpatialStreams = legacy_result->rtt_result_v3.num_tx_sts;
+        aidl_result.numRxSpatialStreams = legacy_result->rtt_result_v3.num_rx_sts;
+        aidl_result.isRangingFrameProtectionEnabled = legacy_result->is_ranging_protection_enabled;
+        aidl_result.isSecureLtfEnabled = legacy_result->is_secure_he_ltf_enabled;
+        aidl_result.baseAkm = convertLegacyAkmsToAidl(legacy_result->base_akm);
+        aidl_result.cipherSuite = convertLegacyCipherSuitesToAidl(legacy_result->cipher_suite);
+        aidl_result.secureHeLtfProtocolVersion = legacy_result->secure_he_ltf_protocol_version;
+        aidl_result.pasnComebackAfterMillis = legacy_result->pasn_comeback_after_millis;
+        if (legacy_result->pasn_comeback_cookie_len > 0 &&
+            legacy_result->pasn_comeback_cookie_len <= RTT_MAX_COOKIE_LEN) {
+            aidl_result.pasnComebackCookie = std::vector<uint8_t>(
+                    legacy_result->pasn_comeback_cookie,
+                    legacy_result->pasn_comeback_cookie + legacy_result->pasn_comeback_cookie_len);
+        }
         aidl_results->push_back(aidl_result);
     }
     return true;
@@ -3627,7 +3883,8 @@
         return false;
     }
     *aidl_scan_result = {};
-    aidl_scan_result->timeStampInUs = ts_us - legacy_scan_result.age_ms * 1000;
+    aidl_scan_result->timeStampInUs =
+            ts_us - (static_cast<uint64_t>(legacy_scan_result.age_ms) * 1000);
     if (aidl_scan_result->timeStampInUs < 0) {
         aidl_scan_result->timeStampInUs = 0;
         return false;
diff --git a/wifi/aidl/default/aidl_struct_util.h b/wifi/aidl/default/aidl_struct_util.h
index 9a3c535..b6a06db 100644
--- a/wifi/aidl/default/aidl_struct_util.h
+++ b/wifi/aidl/default/aidl_struct_util.h
@@ -17,6 +17,8 @@
 #ifndef AIDL_STRUCT_UTIL_H_
 #define AIDL_STRUCT_UTIL_H_
 
+#include <aidl/android/hardware/wifi/Akm.h>
+#include <aidl/android/hardware/wifi/CipherSuite.h>
 #include <aidl/android/hardware/wifi/IWifiChip.h>
 #include <aidl/android/hardware/wifi/IWifiChipEventCallback.h>
 #include <aidl/android/hardware/wifi/NanBandIndex.h>
@@ -153,6 +155,10 @@
         const std::vector<RttConfig>& aidl_configs,
         std::vector<legacy_hal::wifi_rtt_config_v3>* legacy_configs);
 
+bool convertAidlVectorOfRttConfigToLegacyV4(
+        const std::vector<RttConfig>& aidl_configs,
+        std::vector<legacy_hal::wifi_rtt_config_v4>* legacy_configs);
+
 bool convertAidlRttLciInformationToLegacy(const RttLciInformation& aidl_info,
                                           legacy_hal::wifi_lci_information* legacy_info);
 bool convertAidlRttLcrInformationToLegacy(const RttLcrInformation& aidl_info,
@@ -169,6 +175,9 @@
 bool convertLegacyRttCapabilitiesV3ToAidl(
         const legacy_hal::wifi_rtt_capabilities_v3& legacy_capabilities_v3,
         RttCapabilities* aidl_capabilities);
+bool convertLegacyRttCapabilitiesV4ToAidl(
+        const legacy_hal::wifi_rtt_capabilities_v4& legacy_capabilities_v4,
+        RttCapabilities* aidl_capabilities);
 
 bool convertLegacyVectorOfRttResultToAidl(
         const std::vector<const legacy_hal::wifi_rtt_result*>& legacy_results,
@@ -179,6 +188,9 @@
 bool convertLegacyVectorOfRttResultV3ToAidl(
         const std::vector<const legacy_hal::wifi_rtt_result_v3*>& legacy_results,
         std::vector<RttResult>* aidl_results);
+bool convertLegacyVectorOfRttResultV4ToAidl(
+        const std::vector<const legacy_hal::wifi_rtt_result_v4*>& legacy_results,
+        std::vector<RttResult>* aidl_results);
 uint32_t convertAidlWifiBandToLegacyMacBand(WifiBand band);
 uint32_t convertAidlWifiIfaceModeToLegacy(uint32_t aidl_iface_mask);
 uint32_t convertAidlUsableChannelFilterToLegacy(uint32_t aidl_filter_mask);
diff --git a/wifi/aidl/default/wifi_legacy_hal.cpp b/wifi/aidl/default/wifi_legacy_hal.cpp
index 8d69013..c6d6177 100644
--- a/wifi/aidl/default/wifi_legacy_hal.cpp
+++ b/wifi/aidl/default/wifi_legacy_hal.cpp
@@ -185,11 +185,14 @@
         on_rtt_results_internal_callback_v2;
 std::function<void(wifi_request_id, unsigned num_results, wifi_rtt_result_v3* rtt_results_v3[])>
         on_rtt_results_internal_callback_v3;
+std::function<void(wifi_request_id, unsigned num_results, wifi_rtt_result_v4* rtt_results_v4[])>
+        on_rtt_results_internal_callback_v4;
 
 void invalidateRttResultsCallbacks() {
     on_rtt_results_internal_callback = nullptr;
     on_rtt_results_internal_callback_v2 = nullptr;
     on_rtt_results_internal_callback_v3 = nullptr;
+    on_rtt_results_internal_callback_v4 = nullptr;
 };
 
 void onAsyncRttResults(wifi_request_id id, unsigned num_results, wifi_rtt_result* rtt_results[]) {
@@ -218,6 +221,15 @@
     }
 }
 
+void onAsyncRttResultsV4(wifi_request_id id, unsigned num_results,
+                         wifi_rtt_result_v4* rtt_results_v4[]) {
+    const auto lock = aidl_sync_util::acquireGlobalLock();
+    if (on_rtt_results_internal_callback_v4) {
+        on_rtt_results_internal_callback_v4(id, num_results, rtt_results_v4);
+        invalidateRttResultsCallbacks();
+    }
+}
+
 // Callbacks for the various NAN operations.
 // NOTE: These have very little conversions to perform before invoking the user
 // callbacks.
@@ -1344,6 +1356,38 @@
     return status;
 }
 
+wifi_error WifiLegacyHal::startRttRangeRequestV4(
+        const std::string& iface_name, wifi_request_id id,
+        const std::vector<wifi_rtt_config_v4>& rtt_configs,
+        const on_rtt_results_callback_v4& on_results_user_callback_v4) {
+    if (on_rtt_results_internal_callback_v4) {
+        return WIFI_ERROR_NOT_AVAILABLE;
+    }
+
+    on_rtt_results_internal_callback_v4 = [on_results_user_callback_v4](
+                                                  wifi_request_id id, unsigned num_results,
+                                                  wifi_rtt_result_v4* rtt_results_v4[]) {
+        if (num_results > 0 && !rtt_results_v4) {
+            LOG(ERROR) << "Unexpected nullptr in RTT v4 results";
+            return;
+        }
+        std::vector<const wifi_rtt_result_v4*> rtt_results_vec_v4;
+        std::copy_if(rtt_results_v4, rtt_results_v4 + num_results,
+                     back_inserter(rtt_results_vec_v4),
+                     [](wifi_rtt_result_v4* rtt_result_v4) { return rtt_result_v4 != nullptr; });
+        on_results_user_callback_v4(id, rtt_results_vec_v4);
+    };
+
+    std::vector<wifi_rtt_config_v4> rtt_configs_internal(rtt_configs);
+    wifi_error status = global_func_table_.wifi_rtt_range_request_v4(
+            id, getIfaceHandle(iface_name), rtt_configs.size(), rtt_configs_internal.data(),
+            {onAsyncRttResultsV4});
+    if (status != WIFI_SUCCESS) {
+        invalidateRttResultsCallbacks();
+    }
+    return status;
+}
+
 wifi_error WifiLegacyHal::startRttRangeRequestV3(
         const std::string& iface_name, wifi_request_id id,
         const std::vector<wifi_rtt_config_v3>& rtt_configs,
@@ -1460,6 +1504,14 @@
     return {status, rtt_caps_v3};
 }
 
+std::pair<wifi_error, wifi_rtt_capabilities_v4> WifiLegacyHal::getRttCapabilitiesV4(
+        const std::string& iface_name) {
+    wifi_rtt_capabilities_v4 rtt_caps_v4;
+    wifi_error status = global_func_table_.wifi_get_rtt_capabilities_v4(getIfaceHandle(iface_name),
+                                                                        &rtt_caps_v4);
+    return {status, rtt_caps_v4};
+}
+
 std::pair<wifi_error, wifi_rtt_responder> WifiLegacyHal::getRttResponderInfo(
         const std::string& iface_name) {
     wifi_rtt_responder rtt_responder;
diff --git a/wifi/aidl/default/wifi_legacy_hal.h b/wifi/aidl/default/wifi_legacy_hal.h
index f603210..46bf790 100644
--- a/wifi/aidl/default/wifi_legacy_hal.h
+++ b/wifi/aidl/default/wifi_legacy_hal.h
@@ -350,6 +350,7 @@
 using ::wifi_ring_buffer_status;
 using ::wifi_roaming_capabilities;
 using ::wifi_roaming_config;
+using ::wifi_rtt_akm;
 using ::wifi_rtt_bw;
 using ::WIFI_RTT_BW_10;
 using ::WIFI_RTT_BW_160;
@@ -361,8 +362,11 @@
 using ::WIFI_RTT_BW_UNSPECIFIED;
 using ::wifi_rtt_capabilities;
 using ::wifi_rtt_capabilities_v3;
+using ::wifi_rtt_capabilities_v4;
+using ::wifi_rtt_cipher_suite;
 using ::wifi_rtt_config;
 using ::wifi_rtt_config_v3;
+using ::wifi_rtt_config_v4;
 using ::wifi_rtt_preamble;
 using ::WIFI_RTT_PREAMBLE_EHT;
 using ::WIFI_RTT_PREAMBLE_HE;
@@ -374,6 +378,7 @@
 using ::wifi_rtt_result;
 using ::wifi_rtt_result_v2;
 using ::wifi_rtt_result_v3;
+using ::wifi_rtt_result_v4;
 using ::wifi_rtt_status;
 using ::wifi_rtt_type;
 using ::wifi_rx_packet_fate;
@@ -399,6 +404,20 @@
 using ::WLAN_MAC_5_0_BAND;
 using ::WLAN_MAC_60_0_BAND;
 using ::WLAN_MAC_6_0_BAND;
+using ::WPA_CIPHER_CCMP_128;
+using ::WPA_CIPHER_CCMP_256;
+using ::WPA_CIPHER_GCMP_128;
+using ::WPA_CIPHER_GCMP_256;
+using ::WPA_CIPHER_NONE;
+using ::WPA_KEY_MGMT_EAP_FILS_SHA256;
+using ::WPA_KEY_MGMT_EAP_FILS_SHA384;
+using ::WPA_KEY_MGMT_EAP_FT_SHA256;
+using ::WPA_KEY_MGMT_EAP_FT_SHA384;
+using ::WPA_KEY_MGMT_FT_PSK_SHA256;
+using ::WPA_KEY_MGMT_FT_PSK_SHA384;
+using ::WPA_KEY_MGMT_NONE;
+using ::WPA_KEY_MGMT_PASN;
+using ::WPA_KEY_MGMT_SAE;
 
 // APF capabilities supported by the iface.
 struct PacketFilterCapabilities {
@@ -517,6 +536,8 @@
         std::function<void(wifi_request_id, const std::vector<const wifi_rtt_result_v2*>&)>;
 using on_rtt_results_callback_v3 =
         std::function<void(wifi_request_id, const std::vector<const wifi_rtt_result_v3*>&)>;
+using on_rtt_results_callback_v4 =
+        std::function<void(wifi_request_id, const std::vector<const wifi_rtt_result_v4*>&)>;
 
 // Callback for ring buffer data.
 using on_ring_buffer_data_callback = std::function<void(
@@ -705,12 +726,17 @@
     wifi_error startRttRangeRequestV3(const std::string& iface_name, wifi_request_id id,
                                       const std::vector<wifi_rtt_config_v3>& rtt_configs,
                                       const on_rtt_results_callback_v3& on_results_callback);
+    wifi_error startRttRangeRequestV4(const std::string& iface_name, wifi_request_id id,
+                                      const std::vector<wifi_rtt_config_v4>& rtt_configs,
+                                      const on_rtt_results_callback_v4& on_results_callback);
 
     wifi_error cancelRttRangeRequest(const std::string& iface_name, wifi_request_id id,
                                      const std::vector<std::array<uint8_t, ETH_ALEN>>& mac_addrs);
     std::pair<wifi_error, wifi_rtt_capabilities> getRttCapabilities(const std::string& iface_name);
     std::pair<wifi_error, wifi_rtt_capabilities_v3> getRttCapabilitiesV3(
             const std::string& iface_name);
+    std::pair<wifi_error, wifi_rtt_capabilities_v4> getRttCapabilitiesV4(
+            const std::string& iface_name);
     std::pair<wifi_error, wifi_rtt_responder> getRttResponderInfo(const std::string& iface_name);
     wifi_error enableRttResponder(const std::string& iface_name, wifi_request_id id,
                                   const wifi_channel_info& channel_hint, uint32_t max_duration_secs,
diff --git a/wifi/aidl/default/wifi_legacy_hal_stubs.cpp b/wifi/aidl/default/wifi_legacy_hal_stubs.cpp
index 878abf0..d39894e 100644
--- a/wifi/aidl/default/wifi_legacy_hal_stubs.cpp
+++ b/wifi/aidl/default/wifi_legacy_hal_stubs.cpp
@@ -180,7 +180,9 @@
     populateStubFor(&hal_fn->wifi_set_mlo_mode);
     populateStubFor(&hal_fn->wifi_get_supported_iface_concurrency_matrix);
     populateStubFor(&hal_fn->wifi_get_rtt_capabilities_v3);
+    populateStubFor(&hal_fn->wifi_get_rtt_capabilities_v4);
     populateStubFor(&hal_fn->wifi_rtt_range_request_v3);
+    populateStubFor(&hal_fn->wifi_rtt_range_request_v4);
     populateStubFor(&hal_fn->wifi_twt_get_capabilities);
     populateStubFor(&hal_fn->wifi_twt_register_events);
     populateStubFor(&hal_fn->wifi_twt_session_setup);
diff --git a/wifi/aidl/default/wifi_rtt_controller.cpp b/wifi/aidl/default/wifi_rtt_controller.cpp
index 9dee45c..99dafe8 100644
--- a/wifi/aidl/default/wifi_rtt_controller.cpp
+++ b/wifi/aidl/default/wifi_rtt_controller.cpp
@@ -136,13 +136,46 @@
 
 ndk::ScopedAStatus WifiRttController::rangeRequestInternal(
         int32_t cmd_id, const std::vector<RttConfig>& rtt_configs) {
-    // Try 11mc & 11az ranging (v3)
+    // Try 11az secure, 11az non-secure & 11mc ranging (v4)
+    std::vector<legacy_hal::wifi_rtt_config_v4> legacy_configs_v4;
+    if (!aidl_struct_util::convertAidlVectorOfRttConfigToLegacyV4(rtt_configs,
+                                                                  &legacy_configs_v4)) {
+        return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+    }
+    std::weak_ptr<WifiRttController> weak_ptr_this = weak_ptr_this_;
+    const auto& on_results_callback_v4 =
+            [weak_ptr_this](legacy_hal::wifi_request_id id,
+                            const std::vector<const legacy_hal::wifi_rtt_result_v4*>& results) {
+                const auto shared_ptr_this = weak_ptr_this.lock();
+                if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+                    LOG(ERROR) << "v4 Callback invoked on an invalid object";
+                    return;
+                }
+                std::vector<RttResult> aidl_results;
+                if (!aidl_struct_util::convertLegacyVectorOfRttResultV4ToAidl(results,
+                                                                              &aidl_results)) {
+                    LOG(ERROR) << "Failed to convert rtt results v4 to AIDL structs";
+                    return;
+                }
+                for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+                    if (!callback->onResults(id, aidl_results).isOk()) {
+                        LOG(ERROR) << "Failed to invoke the v4 callback";
+                    }
+                }
+            };
+    legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->startRttRangeRequestV4(
+            ifname_, cmd_id, legacy_configs_v4, on_results_callback_v4);
+
+    if (legacy_status != legacy_hal::WIFI_ERROR_NOT_SUPPORTED) {
+        return createWifiStatusFromLegacyError(legacy_status);
+    }
+
+    //  Fallback to 11az non-secure & 11mc ranging (v3)
     std::vector<legacy_hal::wifi_rtt_config_v3> legacy_configs_v3;
     if (!aidl_struct_util::convertAidlVectorOfRttConfigToLegacyV3(rtt_configs,
                                                                   &legacy_configs_v3)) {
         return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
     }
-    std::weak_ptr<WifiRttController> weak_ptr_this = weak_ptr_this_;
     const auto& on_results_callback_v3 =
             [weak_ptr_this](legacy_hal::wifi_request_id id,
                             const std::vector<const legacy_hal::wifi_rtt_result_v3*>& results) {
@@ -163,8 +196,8 @@
                     }
                 }
             };
-    legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->startRttRangeRequestV3(
-            ifname_, cmd_id, legacy_configs_v3, on_results_callback_v3);
+    legacy_status = legacy_hal_.lock()->startRttRangeRequestV3(ifname_, cmd_id, legacy_configs_v3,
+                                                               on_results_callback_v3);
 
     if (legacy_status != legacy_hal::WIFI_ERROR_NOT_SUPPORTED) {
         return createWifiStatusFromLegacyError(legacy_status);
@@ -236,31 +269,46 @@
 std::pair<RttCapabilities, ndk::ScopedAStatus> WifiRttController::getCapabilitiesInternal() {
     legacy_hal::wifi_error legacy_status;
     legacy_hal::wifi_rtt_capabilities_v3 legacy_caps_v3;
-    std::tie(legacy_status, legacy_caps_v3) = legacy_hal_.lock()->getRttCapabilitiesV3(ifname_);
-    // Try v3 API first, if it is not supported fallback.
-    if (legacy_status == legacy_hal::WIFI_ERROR_NOT_SUPPORTED) {
-        legacy_hal::wifi_rtt_capabilities legacy_caps;
-        std::tie(legacy_status, legacy_caps) = legacy_hal_.lock()->getRttCapabilities(ifname_);
-        if (legacy_status != legacy_hal::WIFI_SUCCESS) {
-            return {RttCapabilities{}, createWifiStatusFromLegacyError(legacy_status)};
-        }
+    legacy_hal::wifi_rtt_capabilities_v4 legacy_caps_v4;
 
+    // Try v4 first
+    std::tie(legacy_status, legacy_caps_v4) = legacy_hal_.lock()->getRttCapabilitiesV4(ifname_);
+    if (legacy_status == legacy_hal::WIFI_SUCCESS) {
         RttCapabilities aidl_caps;
-        if (!aidl_struct_util::convertLegacyRttCapabilitiesToAidl(legacy_caps, &aidl_caps)) {
+        if (!aidl_struct_util::convertLegacyRttCapabilitiesV4ToAidl(legacy_caps_v4, &aidl_caps)) {
             return {RttCapabilities{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
         }
         return {aidl_caps, ndk::ScopedAStatus::ok()};
     }
 
-    if (legacy_status != legacy_hal::WIFI_SUCCESS) {
-        return {RttCapabilities{}, createWifiStatusFromLegacyError(legacy_status)};
+    // If not supported, fallback to v3
+    if (legacy_status == legacy_hal::WIFI_ERROR_NOT_SUPPORTED) {
+        std::tie(legacy_status, legacy_caps_v3) = legacy_hal_.lock()->getRttCapabilitiesV3(ifname_);
+        if (legacy_status == legacy_hal::WIFI_SUCCESS) {
+            RttCapabilities aidl_caps;
+            if (!aidl_struct_util::convertLegacyRttCapabilitiesV3ToAidl(legacy_caps_v3,
+                                                                        &aidl_caps)) {
+                return {RttCapabilities{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+            }
+            return {aidl_caps, ndk::ScopedAStatus::ok()};
+        }
     }
 
-    RttCapabilities aidl_caps;
-    if (!aidl_struct_util::convertLegacyRttCapabilitiesV3ToAidl(legacy_caps_v3, &aidl_caps)) {
-        return {RttCapabilities{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+    // If not supported, fallback to default
+    if (legacy_status == legacy_hal::WIFI_ERROR_NOT_SUPPORTED) {
+        legacy_hal::wifi_rtt_capabilities legacy_caps;
+        std::tie(legacy_status, legacy_caps) = legacy_hal_.lock()->getRttCapabilities(ifname_);
+        if (legacy_status == legacy_hal::WIFI_SUCCESS) {
+            RttCapabilities aidl_caps;
+            if (!aidl_struct_util::convertLegacyRttCapabilitiesToAidl(legacy_caps, &aidl_caps)) {
+                return {RttCapabilities{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+            }
+            return {aidl_caps, ndk::ScopedAStatus::ok()};
+        }
     }
-    return {aidl_caps, ndk::ScopedAStatus::ok()};
+
+    // Error, if all failed
+    return {RttCapabilities{}, createWifiStatusFromLegacyError(legacy_status)};
 }
 
 ndk::ScopedAStatus WifiRttController::setLciInternal(int32_t cmd_id, const RttLciInformation& lci) {
diff --git a/wifi/aidl/vts/functional/wifi_aidl_test_utils.cpp b/wifi/aidl/vts/functional/wifi_aidl_test_utils.cpp
index c68d8fd..202082e 100644
--- a/wifi/aidl/vts/functional/wifi_aidl_test_utils.cpp
+++ b/wifi/aidl/vts/functional/wifi_aidl_test_utils.cpp
@@ -276,3 +276,28 @@
     }
     return std::optional<std::vector<std::optional<OuiKeyedData>>>{dataList};
 }
+
+IWifiChip::ApIfaceParams generateApIfaceParams(IfaceConcurrencyType type, bool uses_mlo,
+                                               int oui_size) {
+    IWifiChip::ApIfaceParams params;
+    params.ifaceType = type;
+    params.usesMlo = uses_mlo;
+    params.vendorData = generateOuiKeyedDataListOptional(oui_size);
+    return params;
+}
+
+std::shared_ptr<IWifiApIface> getWifiApOrBridgedApIface(std::shared_ptr<IWifiChip> wifi_chip,
+                                                        IWifiChip::ApIfaceParams params) {
+    if (!wifi_chip.get()) {
+        return nullptr;
+    }
+    std::shared_ptr<IWifiApIface> iface;
+    if (!configureChipToSupportConcurrencyTypeInternal(wifi_chip, IfaceConcurrencyType::AP)) {
+        return nullptr;
+    }
+    auto status = wifi_chip->createApOrBridgedApIfaceWithParams(params, &iface);
+    if (!status.isOk()) {
+        return nullptr;
+    }
+    return iface;
+}
diff --git a/wifi/aidl/vts/functional/wifi_aidl_test_utils.h b/wifi/aidl/vts/functional/wifi_aidl_test_utils.h
index 9b47a9f..6d98bf0 100644
--- a/wifi/aidl/vts/functional/wifi_aidl_test_utils.h
+++ b/wifi/aidl/vts/functional/wifi_aidl_test_utils.h
@@ -43,6 +43,8 @@
 std::shared_ptr<IWifiApIface> getWifiApIface(std::shared_ptr<IWifiChip> wifi_chip);
 std::shared_ptr<IWifiApIface> getBridgedWifiApIface(const char* instance_name);
 std::shared_ptr<IWifiApIface> getBridgedWifiApIface(std::shared_ptr<IWifiChip> wifi_chip);
+std::shared_ptr<IWifiApIface> getWifiApOrBridgedApIface(std::shared_ptr<IWifiChip> wifi_chip,
+                                                        IWifiChip::ApIfaceParams params);
 // Configure the chip in a mode to support the creation of the provided iface type.
 bool configureChipToSupportConcurrencyType(const std::shared_ptr<IWifiChip>& wifi_chip,
                                            IfaceConcurrencyType type, int* configured_mode_id);
@@ -57,3 +59,7 @@
 // Generate test vendor data.
 std::vector<OuiKeyedData> generateOuiKeyedDataList(int size);
 std::optional<std::vector<std::optional<OuiKeyedData>>> generateOuiKeyedDataListOptional(int size);
+
+// Generate test ApIfaceParams
+IWifiChip::ApIfaceParams generateApIfaceParams(IfaceConcurrencyType type, bool uses_mlo,
+                                               int oui_size);
diff --git a/wifi/aidl/vts/functional/wifi_chip_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_chip_aidl_test.cpp
index ab8c6c9..1ef02af 100644
--- a/wifi/aidl/vts/functional/wifi_chip_aidl_test.cpp
+++ b/wifi/aidl/vts/functional/wifi_chip_aidl_test.cpp
@@ -41,6 +41,7 @@
 using aidl::android::hardware::wifi::IWifiP2pIface;
 using aidl::android::hardware::wifi::IWifiRttController;
 using aidl::android::hardware::wifi::WifiBand;
+using aidl::android::hardware::wifi::WifiChipCapabilities;
 using aidl::android::hardware::wifi::WifiDebugHostWakeReasonStats;
 using aidl::android::hardware::wifi::WifiDebugRingBufferStatus;
 using aidl::android::hardware::wifi::WifiDebugRingBufferVerboseLevel;
@@ -55,6 +56,7 @@
         stopWifiService(getInstanceName());
         wifi_chip_ = getWifiChip(getInstanceName());
         ASSERT_NE(nullptr, wifi_chip_.get());
+        ASSERT_TRUE(wifi_chip_->getInterfaceVersion(&interface_version_).isOk());
     }
 
     void TearDown() override { stopWifiService(getInstanceName()); }
@@ -142,6 +144,7 @@
     const char* getInstanceName() { return GetParam().c_str(); }
 
     std::shared_ptr<IWifiChip> wifi_chip_;
+    int interface_version_;
 };
 
 class WifiChipEventCallback : public BnWifiChipEventCallback {
@@ -922,6 +925,112 @@
     }
 }
 
+/**
+ * CreateApOrBridgedApIfaceWithParams for signal ap.
+ */
+TEST_P(WifiChipAidlTest, CreateApOrBridgedApIfaceWithParams_signal_ap) {
+    if (interface_version_ < 3) {
+        GTEST_SKIP() << "CreateApOrBridgedApIfaceWithParams is available as of WifiChip V3";
+    }
+    if (!isConcurrencyTypeSupported(IfaceConcurrencyType::AP)) {
+        GTEST_SKIP() << "AP is not supported";
+    }
+
+    std::shared_ptr<IWifiChip> wifi_chip = getWifiChip(getInstanceName());
+    ASSERT_NE(nullptr, wifi_chip.get());
+    std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApOrBridgedApIface(
+            wifi_chip, generateApIfaceParams(IfaceConcurrencyType::AP, false, 0));
+    ASSERT_NE(nullptr, wifi_ap_iface.get());
+}
+
+/**
+ * CreateApOrBridgedApIfaceWithParams for non mlo bridged ap.
+ */
+TEST_P(WifiChipAidlTest, CreateApOrBridgedApIfaceWithParams_non_mlo_bridged_ap) {
+    if (interface_version_ < 3) {
+        GTEST_SKIP() << "CreateApOrBridgedApIfaceWithParams is available as of WifiChip V3";
+    }
+    bool isBridgedSupport = testing::checkSubstringInCommandOutput(
+            "/system/bin/cmd wifi get-softap-supported-features",
+            "wifi_softap_bridged_ap_supported");
+    if (!isBridgedSupport) {
+        GTEST_SKIP() << "Missing Bridged AP support";
+    }
+
+    std::shared_ptr<IWifiChip> wifi_chip = getWifiChip(getInstanceName());
+    ASSERT_NE(nullptr, wifi_chip.get());
+    std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApOrBridgedApIface(
+            wifi_chip, generateApIfaceParams(IfaceConcurrencyType::AP_BRIDGED, false, 0));
+    ASSERT_NE(nullptr, wifi_ap_iface.get());
+
+    std::string br_name;
+    std::vector<std::string> instances;
+    bool uses_mlo;
+    EXPECT_TRUE(wifi_ap_iface->getName(&br_name).isOk());
+    EXPECT_TRUE(wifi_ap_iface->getBridgedInstances(&instances).isOk());
+    EXPECT_TRUE(wifi_ap_iface->usesMlo(&uses_mlo).isOk());
+    EXPECT_FALSE(uses_mlo);
+    EXPECT_EQ(instances.size(), 2);
+}
+
+/**
+ * CreateApOrBridgedApIfaceWithParams for mlo bridged ap.
+ */
+TEST_P(WifiChipAidlTest, CreateApOrBridgedApIfaceWithParams_mlo_bridged_ap) {
+    if (interface_version_ < 3) {
+        GTEST_SKIP() << "CreateApOrBridgedApIfaceWithParams is available as of WifiChip V3";
+    }
+    bool isBridgedSupport = testing::checkSubstringInCommandOutput(
+            "/system/bin/cmd wifi get-softap-supported-features",
+            "wifi_softap_bridged_ap_supported");
+    if (!isBridgedSupport) {
+        GTEST_SKIP() << "Missing Bridged AP support";
+    }
+
+    configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+    int32_t features = getChipFeatureSet(wifi_chip_);
+    if (!(features & static_cast<int32_t>(IWifiChip::FeatureSetMask::MLO_SAP))) {
+        GTEST_SKIP() << "MLO_SAP is not supported by vendor.";
+    }
+    std::shared_ptr<IWifiChip> wifi_chip = getWifiChip(getInstanceName());
+    ASSERT_NE(nullptr, wifi_chip.get());
+    std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApOrBridgedApIface(
+            wifi_chip, generateApIfaceParams(IfaceConcurrencyType::AP_BRIDGED, true, 0));
+    ASSERT_NE(nullptr, wifi_ap_iface.get());
+
+    std::string br_name;
+    std::vector<std::string> instances;
+    bool uses_mlo;
+    EXPECT_TRUE(wifi_ap_iface->getName(&br_name).isOk());
+    EXPECT_TRUE(wifi_ap_iface->getBridgedInstances(&instances).isOk());
+    EXPECT_TRUE(wifi_ap_iface->usesMlo(&uses_mlo).isOk());
+    EXPECT_TRUE(uses_mlo);
+    EXPECT_EQ(instances.size(), 2);
+}
+
+/*
+ * GetWifiChipCapabilities
+ */
+TEST_P(WifiChipAidlTest, GetWifiChipCapabilities) {
+    WifiChipCapabilities chipCapabilities;
+    auto status = wifi_chip_->getWifiChipCapabilities(&chipCapabilities);
+    if (checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
+        GTEST_SKIP() << "getWifiChipCapabilities() is not supported by vendor.";
+    }
+    EXPECT_TRUE(status.isOk());
+}
+
+/*
+ * SetMloMode
+ */
+TEST_P(WifiChipAidlTest, SetMloMode) {
+    auto status = wifi_chip_->setMloMode(IWifiChip::ChipMloMode::LOW_LATENCY);
+    if (checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
+        GTEST_SKIP() << "setMloMode() is not supported by vendor.";
+    }
+    EXPECT_TRUE(status.isOk());
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WifiChipAidlTest);
 INSTANTIATE_TEST_SUITE_P(WifiTest, WifiChipAidlTest,
                          testing::ValuesIn(android::getAidlHalInstanceNames(IWifi::descriptor)),
diff --git a/wifi/aidl/vts/functional/wifi_rtt_controller_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_rtt_controller_aidl_test.cpp
index 1596602..aca1364 100644
--- a/wifi/aidl/vts/functional/wifi_rtt_controller_aidl_test.cpp
+++ b/wifi/aidl/vts/functional/wifi_rtt_controller_aidl_test.cpp
@@ -19,8 +19,10 @@
 #include <VtsCoreUtil.h>
 #include <aidl/Gtest.h>
 #include <aidl/Vintf.h>
+#include <aidl/android/hardware/wifi/Akm.h>
 #include <aidl/android/hardware/wifi/BnWifi.h>
 #include <aidl/android/hardware/wifi/BnWifiRttControllerEventCallback.h>
+#include <aidl/android/hardware/wifi/RttSecureConfig.h>
 #include <android-base/logging.h>
 #include <android/binder_manager.h>
 #include <android/binder_status.h>
@@ -29,6 +31,7 @@
 
 #include "wifi_aidl_test_utils.h"
 
+using aidl::android::hardware::wifi::Akm;
 using aidl::android::hardware::wifi::BnWifiRttControllerEventCallback;
 using aidl::android::hardware::wifi::IWifiRttController;
 using aidl::android::hardware::wifi::RttBw;
@@ -38,6 +41,7 @@
 using aidl::android::hardware::wifi::RttPreamble;
 using aidl::android::hardware::wifi::RttResponder;
 using aidl::android::hardware::wifi::RttResult;
+using aidl::android::hardware::wifi::RttSecureConfig;
 using aidl::android::hardware::wifi::RttType;
 using aidl::android::hardware::wifi::WifiChannelInfo;
 using aidl::android::hardware::wifi::WifiChannelWidthInMhz;
@@ -87,6 +91,15 @@
         return caps;
     }
 
+    int getMostSignificantSetBitMask(int n) {
+        if (n == 0) return 0;
+        int pos = std::numeric_limits<int>::digits - 1;
+        while ((n & (1 << pos)) == 0) {
+            pos--;
+        }
+        return 1 << pos;
+    }
+
     std::shared_ptr<IWifiRttController> wifi_rtt_controller_;
     int interface_version_;
 
@@ -161,6 +174,66 @@
 }
 
 /*
+ * Request80211azNtbSecureRangeMeasurement
+ * Tests the two sided 11az non-trigger based secure ranging - 802.11az NTB FTM protocol.
+ */
+TEST_P(WifiRttControllerAidlTest, Request80211azNtbSecureRangeMeasurement) {
+    if (interface_version_ < 3) {
+        GTEST_SKIP() << "Request80211azNtbRangeMeasurement is available as of RttController V3";
+    }
+
+    RttCapabilities caps = getCapabilities();
+    if (!caps.ntbInitiatorSupported) {
+        GTEST_SKIP() << "Skipping 11az NTB RTT since driver/fw does not support";
+    }
+    if (!caps.secureHeLtfSupported && !caps.rangingFrameProtectionSupported) {
+        GTEST_SKIP() << "Skipping 11az NTB secure RTT since driver/fw does not support";
+    }
+    if (!(caps.akmsSupported & Akm::PASN)) {
+        GTEST_SKIP() << "Skipping 11az NTB secure RTT since driver/fw does not support PASN";
+    }
+    if (!caps.cipherSuitesSupported) {
+        GTEST_SKIP()
+                << "Skipping 11az NTB secure RTT since driver/fw does not support Cipher Suites";
+    }
+
+    RttConfig config;
+    config.addr = {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}};
+    config.type = RttType::TWO_SIDED_11AZ_NTB_SECURE;
+    config.peer = RttPeerType::AP;
+    config.channel.width = WifiChannelWidthInMhz::WIDTH_80;
+    config.channel.centerFreq = 5180;
+    config.channel.centerFreq0 = 5210;
+    config.channel.centerFreq1 = 0;
+    config.bw = RttBw::BW_20MHZ;
+    config.preamble = RttPreamble::HT;
+    config.mustRequestLci = false;
+    config.mustRequestLcr = false;
+    config.numFramesPerBurst = 8;
+    config.numRetriesPerRttFrame = 0;
+    config.numRetriesPerFtmr = 0;
+    // 11az non-trigger based minimum measurement time in units of 100 microseconds.
+    config.ntbMinMeasurementTime = 2500;
+    // 11az non-trigger based maximum measurement time in units of 10 milliseconds.
+    config.ntbMaxMeasurementTime = 1500;
+    RttSecureConfig secureConfig;
+    // PASN is a must to test secure config; which does not need any password.
+    secureConfig.pasnConfig.baseAkm = Akm::PASN;
+    // Get the best Cipher suite supported by the chip.
+    secureConfig.pasnConfig.cipherSuite = getMostSignificantSetBitMask(caps.cipherSuitesSupported);
+    secureConfig.enableSecureHeLtf = caps.secureHeLtfSupported;
+    secureConfig.enableRangingFrameProtection = caps.rangingFrameProtectionSupported;
+    config.secureConfig = secureConfig;
+
+    int cmdId = 55;
+    std::vector<RttConfig> configs = {config};
+    EXPECT_TRUE(wifi_rtt_controller_->rangeRequest(cmdId, configs).isOk());
+
+    // Sleep for 2 seconds to wait for driver/firmware to complete RTT.
+    sleep(2);
+}
+
+/*
  * Request80211azNtbRangeMeasurement
  * Tests the two sided 11az non-trigger based ranging - 802.11az NTB FTM protocol.
  */
diff --git a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
index f659bf6..5e8e21f 100644
--- a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
+++ b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
@@ -65,6 +65,12 @@
         return features & static_cast<int32_t>(expected);
     }
 
+    bool isTwtSupported() {
+        TwtCapabilities twt_capabilities = {};
+        auto status = wifi_sta_iface_->twtGetCapabilities(&twt_capabilities);
+        return status.isOk() && twt_capabilities.isTwtRequesterSupported;
+    }
+
     ndk::ScopedAStatus createStaIface(std::shared_ptr<IWifiStaIface>* sta_iface) {
         std::shared_ptr<IWifiChip> wifi_chip = getWifiChip(getInstanceName());
         EXPECT_NE(nullptr, wifi_chip.get());
@@ -379,6 +385,9 @@
     }
 }
 
+/**
+ * TwtGetCapabilities
+ */
 TEST_P(WifiStaIfaceAidlTest, TwtGetCapabilities) {
     if (interface_version_ < 2) {
         GTEST_SKIP() << "TwtGetCapabilities is available as of sta_iface V2";
@@ -400,6 +409,9 @@
     EXPECT_GT(twt_capabilities.maxWakeIntervalUs, 0);
 }
 
+/**
+ * TwtSessionSetup
+ */
 TEST_P(WifiStaIfaceAidlTest, TwtSessionSetup) {
     if (interface_version_ < 2) {
         GTEST_SKIP() << "TwtSessionSetup is available as of sta_iface V2";
@@ -424,18 +436,14 @@
     EXPECT_TRUE(wifi_sta_iface_->twtSessionSetup(1, twtRequest).isOk());
 }
 
+/**
+ * TwtSessionGetStats
+ */
 TEST_P(WifiStaIfaceAidlTest, TwtSessionGetStats) {
     if (interface_version_ < 2) {
         GTEST_SKIP() << "TwtSessionGetStats is available as of sta_iface V2";
     }
-
-    TwtCapabilities twt_capabilities = {};
-    auto status = wifi_sta_iface_->twtGetCapabilities(&twt_capabilities);
-    if (checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
-        GTEST_SKIP() << "twtGetCapabilities() is not supported by the vendor";
-    }
-    EXPECT_TRUE(status.isOk());
-    if (!twt_capabilities.isTwtRequesterSupported) {
+    if (!isTwtSupported()) {
         GTEST_SKIP() << "TWT is not supported";
     }
 
@@ -444,26 +452,77 @@
     EXPECT_TRUE(wifi_sta_iface_->twtSessionGetStats(1, 10).isOk());
 }
 
+/**
+ * TwtSessionTeardown
+ */
 TEST_P(WifiStaIfaceAidlTest, TwtSessionTeardown) {
     if (interface_version_ < 2) {
-        GTEST_SKIP() << "TwtSessionTeardown is available as of sta_iface V3";
+        GTEST_SKIP() << "TwtSessionTeardown is available as of sta_iface V2";
     }
-
-    TwtCapabilities twt_capabilities = {};
-    auto status = wifi_sta_iface_->twtGetCapabilities(&twt_capabilities);
-    if (checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
-        GTEST_SKIP() << "twtGetCapabilities() is not supported by the vendor";
-    }
-    EXPECT_TRUE(status.isOk());
-    if (!twt_capabilities.isTwtRequesterSupported) {
+    if (!isTwtSupported()) {
         GTEST_SKIP() << "TWT is not supported";
     }
 
     // Expecting a IWifiStaIfaceEventCallback.onTwtFailure() with INVALID_PARAMS
-    // as  the error code.
+    // as the error code.
     EXPECT_TRUE(wifi_sta_iface_->twtSessionTeardown(1, 10).isOk());
 }
 
+/**
+ * TwtSessionUpdate
+ */
+TEST_P(WifiStaIfaceAidlTest, TwtSessionUpdate) {
+    if (interface_version_ < 2) {
+        GTEST_SKIP() << "TwtSessionUpdate is available as of sta_iface V2";
+    }
+    if (!isTwtSupported()) {
+        GTEST_SKIP() << "TWT is not supported";
+    }
+
+    TwtRequest twtRequest;
+    twtRequest.mloLinkId = 0;
+    twtRequest.minWakeDurationUs = 1000;
+    twtRequest.maxWakeDurationUs = 10000;
+    twtRequest.minWakeIntervalUs = 10000;
+    twtRequest.maxWakeIntervalUs = 100000;
+
+    // Expecting a IWifiStaIfaceEventCallback.onTwtFailure() with INVALID_PARAMS
+    // as the error code.
+    EXPECT_TRUE(wifi_sta_iface_->twtSessionUpdate(1, 10, twtRequest).isOk());
+}
+
+/**
+ * TwtSessionSuspend
+ */
+TEST_P(WifiStaIfaceAidlTest, TwtSessionSuspend) {
+    if (interface_version_ < 2) {
+        GTEST_SKIP() << "TwtSessionSuspend is available as of sta_iface V2";
+    }
+    if (!isTwtSupported()) {
+        GTEST_SKIP() << "TWT is not supported";
+    }
+
+    // Expecting a IWifiStaIfaceEventCallback.onTwtFailure() with INVALID_PARAMS
+    // as the error code.
+    EXPECT_TRUE(wifi_sta_iface_->twtSessionSuspend(1, 10).isOk());
+}
+
+/**
+ * TwtSessionResume
+ */
+TEST_P(WifiStaIfaceAidlTest, TwtSessionResume) {
+    if (interface_version_ < 2) {
+        GTEST_SKIP() << "TwtSessionResume is available as of sta_iface V2";
+    }
+    if (!isTwtSupported()) {
+        GTEST_SKIP() << "TWT is not supported";
+    }
+
+    // Expecting a IWifiStaIfaceEventCallback.onTwtFailure() with INVALID_PARAMS
+    // as the error code.
+    EXPECT_TRUE(wifi_sta_iface_->twtSessionResume(1, 10).isOk());
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WifiStaIfaceAidlTest);
 INSTANTIATE_TEST_SUITE_P(WifiTest, WifiStaIfaceAidlTest,
                          testing::ValuesIn(android::getAidlHalInstanceNames(IWifi::descriptor)),
diff --git a/wifi/hostapd/aidl/vts/functional/VtsHalHostapdTargetTest.cpp b/wifi/hostapd/aidl/vts/functional/VtsHalHostapdTargetTest.cpp
index 590c58b..6723b1f 100644
--- a/wifi/hostapd/aidl/vts/functional/VtsHalHostapdTargetTest.cpp
+++ b/wifi/hostapd/aidl/vts/functional/VtsHalHostapdTargetTest.cpp
@@ -86,6 +86,8 @@
         isBridgedSupport = testing::checkSubstringInCommandOutput(
                 "/system/bin/cmd wifi get-softap-supported-features",
                 "wifi_softap_bridged_ap_supported");
+        isMloSupport = testing::checkSubstringInCommandOutput(
+                "/system/bin/cmd wifi get-softap-supported-features", "wifi_softap_mlo_supported");
     }
 
     virtual void TearDown() override {
@@ -100,6 +102,7 @@
     bool isAcsSupport;
     bool isWpa3SaeSupport;
     bool isBridgedSupport;
+    bool isMloSupport;
     int interface_version_;
 
     IfaceParams getIfaceParamsWithoutAcs(std::string iface_name) {
@@ -476,6 +479,34 @@
     EXPECT_TRUE(status.isOk());
 }
 
+/**
+ * AddAccessPointWithMloConfig and remove link should pass
+ */
+TEST_P(HostapdAidl, AddAccessPointWithMloConfigAndRemoveInstance) {
+    if (interface_version_ < 3) {
+        GTEST_SKIP() << "MLO SAP is available in IfaceParams as of Hostapd V3";
+    }
+    if (!isMloSupport) GTEST_SKIP() << "Missing MLO AP support";
+    std::shared_ptr<IWifiApIface> wifi_ap_iface = HostapdAidlTestUtils::setupMloApIface();
+    EXPECT_TRUE(wifi_ap_iface.get() != nullptr);
+    std::string br_name;
+    std::vector<std::string> instances;
+    bool uses_mlo;
+    EXPECT_TRUE(wifi_ap_iface->getName(&br_name).isOk());
+    EXPECT_TRUE(wifi_ap_iface->getBridgedInstances(&instances).isOk());
+    EXPECT_TRUE(wifi_ap_iface->usesMlo(&uses_mlo).isOk());
+    EXPECT_TRUE(uses_mlo);
+
+    IfaceParams iface_params = getIfaceParamsWithBridgedModeACS(br_name);
+    iface_params.instanceIdentities = {instances[0], instances[1]};
+    iface_params.usesMlo = uses_mlo;
+    iface_params.hwModeParams.enable80211AX = true;
+    iface_params.hwModeParams.enable80211BE = true;
+
+    EXPECT_TRUE(hostapd->addAccessPoint(iface_params, getSaeNwParams()).isOk());
+    EXPECT_TRUE(hostapd->removeLinkFromMultipleLinkBridgedApIface(br_name, instances[0]).isOk());
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HostapdAidl);
 INSTANTIATE_TEST_SUITE_P(
     Hostapd, HostapdAidl,
diff --git a/wifi/hostapd/aidl/vts/functional/hostapd_aidl_test_utils.h b/wifi/hostapd/aidl/vts/functional/hostapd_aidl_test_utils.h
index 93540b2..3876998 100644
--- a/wifi/hostapd/aidl/vts/functional/hostapd_aidl_test_utils.h
+++ b/wifi/hostapd/aidl/vts/functional/hostapd_aidl_test_utils.h
@@ -77,4 +77,11 @@
     return ap_iface_name;
 }
 
+std::shared_ptr<IWifiApIface> setupMloApIface() {
+    std::shared_ptr<IWifiChip> wifi_chip = getWifiChip(kWifiInstanceName);
+    EXPECT_TRUE(wifi_chip.get() != nullptr);
+    return getWifiApOrBridgedApIface(
+            wifi_chip, generateApIfaceParams(IfaceConcurrencyType::AP_BRIDGED, true, 0));
+}
+
 }  // namespace HostapdAidlTestUtils
diff --git a/wifi/legacy_headers/include/hardware_legacy/wifi_hal.h b/wifi/legacy_headers/include/hardware_legacy/wifi_hal.h
index c68cdf6..a757954 100644
--- a/wifi/legacy_headers/include/hardware_legacy/wifi_hal.h
+++ b/wifi/legacy_headers/include/hardware_legacy/wifi_hal.h
@@ -790,10 +790,13 @@
             wifi_rtt_config[], wifi_rtt_event_handler);
     wifi_error (* wifi_rtt_range_request_v3)(wifi_request_id, wifi_interface_handle, unsigned,
             wifi_rtt_config_v3[], wifi_rtt_event_handler_v3);
+    wifi_error (*wifi_rtt_range_request_v4)(wifi_request_id, wifi_interface_handle, unsigned,
+                                            wifi_rtt_config_v4[], wifi_rtt_event_handler_v4);
     wifi_error (* wifi_rtt_range_cancel)(wifi_request_id,  wifi_interface_handle, unsigned,
             mac_addr[]);
     wifi_error (* wifi_get_rtt_capabilities)(wifi_interface_handle, wifi_rtt_capabilities *);
     wifi_error (* wifi_get_rtt_capabilities_v3)(wifi_interface_handle, wifi_rtt_capabilities_v3 *);
+    wifi_error (*wifi_get_rtt_capabilities_v4)(wifi_interface_handle, wifi_rtt_capabilities_v4*);
     wifi_error (* wifi_rtt_get_responder_info)(wifi_interface_handle iface,
             wifi_rtt_responder *responder_info);
     wifi_error (* wifi_enable_responder)(wifi_request_id id, wifi_interface_handle iface,
diff --git a/wifi/legacy_headers/include/hardware_legacy/wifi_nan.h b/wifi/legacy_headers/include/hardware_legacy/wifi_nan.h
index cd4e86d..37368af 100644
--- a/wifi/legacy_headers/include/hardware_legacy/wifi_nan.h
+++ b/wifi/legacy_headers/include/hardware_legacy/wifi_nan.h
@@ -477,9 +477,6 @@
     bool is_pairing_supported;
     bool is_set_cluster_id_supported;
     bool is_suspension_supported;
-    bool is_periodic_ranging_supported;
-    wifi_rtt_bw supported_bw;
-    u8 num_rx_chains_supported;
 } NanCapabilities;
 
 /*
@@ -750,12 +747,6 @@
     u32 distance_ingress_mm;
     /* Egress distance in millmilliimeters (optional) */
     u32 distance_egress_mm;
-    /* Number of FTM frames per burst */
-    u32 rtt_burst_size;
-    /* RTT Measurement Preamble */
-    wifi_rtt_preamble preamble;
-    /* Channel information */
-    wifi_channel_info channel_info;
 } NanRangingCfg;
 
 /* NAN Ranging request's response */
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
index 1eb07d5..b0141df 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
@@ -84,10 +84,10 @@
   oneway void onPmkSaCacheAdded(in android.hardware.wifi.supplicant.PmkSaCacheData pmkSaData);
   oneway void onUsdPublishStarted(in int cmdId, in int publishId);
   oneway void onUsdSubscribeStarted(in int cmdId, in int subscribeId);
-  oneway void onUsdPublishConfigFailed(in int cmdId);
-  oneway void onUsdSubscribeConfigFailed(in int cmdId);
-  oneway void onUsdPublishTerminated(in int publishId, in android.hardware.wifi.supplicant.UsdReasonCode reasonCode);
-  oneway void onUsdSubscribeTerminated(in int subscribeId, in android.hardware.wifi.supplicant.UsdReasonCode reasonCode);
+  oneway void onUsdPublishConfigFailed(in int cmdId, in android.hardware.wifi.supplicant.ISupplicantStaIfaceCallback.UsdConfigErrorCode errorCode);
+  oneway void onUsdSubscribeConfigFailed(in int cmdId, in android.hardware.wifi.supplicant.ISupplicantStaIfaceCallback.UsdConfigErrorCode errorCode);
+  oneway void onUsdPublishTerminated(in int publishId, in android.hardware.wifi.supplicant.UsdTerminateReasonCode reasonCode);
+  oneway void onUsdSubscribeTerminated(in int subscribeId, in android.hardware.wifi.supplicant.UsdTerminateReasonCode reasonCode);
   oneway void onUsdPublishReplied(in android.hardware.wifi.supplicant.UsdServiceDiscoveryInfo info);
   oneway void onUsdServiceDiscovered(in android.hardware.wifi.supplicant.UsdServiceDiscoveryInfo info);
   oneway void onUsdMessageReceived(in android.hardware.wifi.supplicant.UsdMessageInfo messageInfo);
@@ -97,4 +97,10 @@
     MULTI_LINK_RECONFIG_AP_REMOVAL = 1,
     MULTI_LINK_DYNAMIC_RECONFIG = 2,
   }
+  @Backing(type="int") @VintfStability
+  enum UsdConfigErrorCode {
+    FAILURE_UNKNOWN = 0,
+    FAILURE_TIMEOUT = 1,
+    FAILURE_NOT_AVAILABLE = 2,
+  }
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdBaseConfig.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdBaseConfig.aidl
index 8d23922..73531e1 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdBaseConfig.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdBaseConfig.aidl
@@ -38,7 +38,7 @@
   android.hardware.wifi.supplicant.UsdServiceProtoType serviceProtoType;
   byte[] serviceSpecificInfo;
   @nullable byte[] txMatchFilter;
-  @nullable byte[] rxMatchfilter;
+  @nullable byte[] rxMatchFilter;
   int ttlSec;
   int defaultFreqMhz;
   int[] freqsMhz;
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdPublishConfig.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdPublishConfig.aidl
index 791de52..99ac16d 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdPublishConfig.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/UsdPublishConfig.aidl
@@ -39,6 +39,7 @@
   boolean isFsd;
   int announcementPeriodMillis;
   android.hardware.wifi.supplicant.UsdPublishTransmissionType transmissionType;
+  boolean eventsEnabled;
   enum PublishType {
     SOLICITED_ONLY = 0,
     UNSOLICITED_ONLY = 1,
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
index 6a3f299..efbd066 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
@@ -37,8 +37,8 @@
 import android.hardware.wifi.supplicant.StaIfaceReasonCode;
 import android.hardware.wifi.supplicant.SupplicantStateChangeData;
 import android.hardware.wifi.supplicant.UsdMessageInfo;
-import android.hardware.wifi.supplicant.UsdReasonCode;
 import android.hardware.wifi.supplicant.UsdServiceDiscoveryInfo;
+import android.hardware.wifi.supplicant.UsdTerminateReasonCode;
 import android.hardware.wifi.supplicant.WpsConfigError;
 import android.hardware.wifi.supplicant.WpsErrorIndication;
 
@@ -439,12 +439,32 @@
     void onUsdSubscribeStarted(in int cmdId, in int subscribeId);
 
     /**
+     * Error codes returned by |onUsdPublishConfigFailed| and |onUsdSubscribeConfigFailed|.
+     */
+    @VintfStability
+    @Backing(type="int")
+    enum UsdConfigErrorCode {
+        /**
+         * Unknown failure.
+         */
+        FAILURE_UNKNOWN = 0,
+        /**
+         * The requested operation timed out.
+         */
+        FAILURE_TIMEOUT = 1,
+        /**
+         * The requested operation is currently not available.
+         */
+        FAILURE_NOT_AVAILABLE = 2,
+    }
+
+    /**
      * Called in response to |ISupplicantStaIface.startUsdPublish| to indicate that the
      * publish session could not be configured.
      *
      * @param cmdId Identifier for the original request.
      */
-    void onUsdPublishConfigFailed(in int cmdId);
+    void onUsdPublishConfigFailed(in int cmdId, in UsdConfigErrorCode errorCode);
 
     /**
      * Called in response to |ISupplicantStaIface.startUsdSubscribe| to indicate that the
@@ -452,7 +472,7 @@
      *
      * @param cmdId Identifier for the original request.
      */
-    void onUsdSubscribeConfigFailed(in int cmdId);
+    void onUsdSubscribeConfigFailed(in int cmdId, in UsdConfigErrorCode errorCode);
 
     /**
      * Called in response to |ISupplicantStaIface.cancelUsdPublish| to indicate that the session
@@ -462,7 +482,7 @@
      * @param publishId Identifier for the publish session.
      * @param reasonCode Code indicating the reason for the session cancellation.
      */
-    void onUsdPublishTerminated(in int publishId, in UsdReasonCode reasonCode);
+    void onUsdPublishTerminated(in int publishId, in UsdTerminateReasonCode reasonCode);
 
     /**
      * Called in response to |ISupplicantStaIface.cancelUsdSubscribe| to indicate that the session
@@ -472,7 +492,7 @@
      * @param subscribeId Identifier for the subscribe session.
      * @param reasonCode Code indicating the reason for the session cancellation.
      */
-    void onUsdSubscribeTerminated(in int subscribeId, in UsdReasonCode reasonCode);
+    void onUsdSubscribeTerminated(in int subscribeId, in UsdTerminateReasonCode reasonCode);
 
     /**
      * Indicates that the publisher sent solicited publish message to the subscriber.
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/UsdBaseConfig.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/UsdBaseConfig.aidl
index f43f27b..68321f6 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/UsdBaseConfig.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/UsdBaseConfig.aidl
@@ -68,7 +68,7 @@
      * Max length: |UsdCapabilities.maxMatchFilterLength|.
      * NAN Spec: matching_filter_rx
      */
-    @nullable byte[] rxMatchfilter;
+    @nullable byte[] rxMatchFilter;
 
     /**
      * Time interval (in seconds) that a USD session will be alive.
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/UsdPublishConfig.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/UsdPublishConfig.aidl
index e04974b..222edce 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/UsdPublishConfig.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/UsdPublishConfig.aidl
@@ -68,4 +68,11 @@
      * Type of the publish transmission (ex. unicast, multicast).
      */
     UsdPublishTransmissionType transmissionType;
+
+    /**
+     * Whether to enable publish replied events. If disabled, then
+     * |ISupplicantStaIfaceCallback.onUsdPublishReplied| will not be
+     * called for this session.
+     */
+    boolean eventsEnabled;
 }
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/UsdReasonCode.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/UsdReasonCode.aidl
deleted file mode 100644
index 4b1aab0..0000000
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/UsdReasonCode.aidl
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wifi.supplicant;
-
-/**
- * Codes indicating the status of USD operations.
- */
-@VintfStability
-@Backing(type="int")
-enum UsdReasonCode {
-    /**
-     * Unknown failure occurred.
-     */
-    FAILURE_UNKNOWN = 0,
-
-    /**
-     * The operation timed out.
-     */
-    TIMEOUT = 1,
-
-    /**
-     * The operation was requested by the user.
-     */
-    USER_REQUESTED = 2,
-
-    /**
-     * Invalid arguments were provided.
-     */
-    INVALID_ARGS = 3
-}
diff --git a/wifi/supplicant/aidl/vts/functional/Android.bp b/wifi/supplicant/aidl/vts/functional/Android.bp
index f94eb46..95ff6cd 100644
--- a/wifi/supplicant/aidl/vts/functional/Android.bp
+++ b/wifi/supplicant/aidl/vts/functional/Android.bp
@@ -137,3 +137,41 @@
         "vts",
     ],
 }
+
+cc_test {
+    name: "VtsHalWifiSupplicantP2pNetworkTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: ["supplicant_p2p_network_aidl_test.cpp"],
+    shared_libs: [
+        "libbinder",
+        "libbinder_ndk",
+        "libvndksupport",
+    ],
+    static_libs: [
+        "android.hardware.wifi@1.0",
+        "android.hardware.wifi@1.1",
+        "android.hardware.wifi@1.2",
+        "android.hardware.wifi@1.3",
+        "android.hardware.wifi@1.4",
+        "android.hardware.wifi@1.5",
+        "android.hardware.wifi.common-V2-ndk",
+        "android.hardware.wifi.supplicant@1.0",
+        "android.hardware.wifi.supplicant@1.1",
+        "android.hardware.wifi.supplicant-V4-ndk",
+        "libwifi-system",
+        "libwifi-system-iface",
+        "VtsHalWifiV1_0TargetTestUtil",
+        "VtsHalWifiV1_5TargetTestUtil",
+        "VtsHalWifiSupplicantV1_0TargetTestUtil",
+        "android.hardware.wifi.common-V2-ndk",
+        "android.hardware.wifi-V3-ndk",
+        "VtsHalWifiTargetTestUtil",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
index 3638ac5..778e20a 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
@@ -38,6 +38,7 @@
 using aidl::android::hardware::wifi::supplicant::IfaceType;
 using aidl::android::hardware::wifi::supplicant::ISupplicant;
 using aidl::android::hardware::wifi::supplicant::ISupplicantP2pIface;
+using aidl::android::hardware::wifi::supplicant::ISupplicantP2pNetwork;
 using aidl::android::hardware::wifi::supplicant::MiracastMode;
 using aidl::android::hardware::wifi::supplicant::P2pAddGroupConfigurationParams;
 using aidl::android::hardware::wifi::supplicant::P2pConnectInfo;
@@ -398,8 +399,8 @@
  * SetWpsModelNumber
  */
 TEST_P(SupplicantP2pIfaceAidlTest, SetWpsModelNumber) {
-    const std::string modelNumber = "TestModelNumber";
-    EXPECT_TRUE(p2p_iface_->setWpsModelName(modelNumber).isOk());
+    const std::string modelNumber = "Model1234";
+    EXPECT_TRUE(p2p_iface_->setWpsModelNumber(modelNumber).isOk());
 }
 
 /*
@@ -981,6 +982,96 @@
     EXPECT_TRUE(p2p_iface_->reinvokePersistentGroup(params).isOk());
 }
 
+/*
+ * Test the P2P network management functions.
+ */
+TEST_P(SupplicantP2pIfaceAidlTest, ManageNetworks) {
+    std::shared_ptr<ISupplicantP2pNetwork> network;
+    EXPECT_TRUE(p2p_iface_->addNetwork(&network).isOk());
+    ASSERT_NE(network, nullptr);
+
+    std::vector<int32_t> networkList;
+    EXPECT_TRUE(p2p_iface_->listNetworks(&networkList).isOk());
+    ASSERT_FALSE(networkList.empty());
+
+    int networkId = networkList[0];
+    EXPECT_TRUE(p2p_iface_->getNetwork(networkId, &network).isOk());
+    ASSERT_NE(network, nullptr);
+    EXPECT_TRUE(p2p_iface_->removeNetwork(networkId).isOk());
+}
+
+/*
+ * Request and cancel service discovery
+ */
+TEST_P(SupplicantP2pIfaceAidlTest, RequestAndCancelServiceDiscovery) {
+    int64_t discoveryId;
+    std::vector<uint8_t> query = {0x11, 0x22, 0x33};
+    EXPECT_TRUE(p2p_iface_->requestServiceDiscovery(kTestMacAddr, query, &discoveryId).isOk());
+    EXPECT_TRUE(p2p_iface_->cancelServiceDiscovery(discoveryId).isOk());
+}
+
+/*
+ * Start and stop WPS
+ */
+TEST_P(SupplicantP2pIfaceAidlTest, StartAndStopWps) {
+    // Expected to fail with test values
+    std::string generatedPin;
+    EXPECT_FALSE(p2p_iface_->startWpsPbc(kTestGroupIfName, kTestMacAddr).isOk());
+    EXPECT_FALSE(
+            p2p_iface_->startWpsPinDisplay(kTestGroupIfName, kTestMacAddr, &generatedPin).isOk());
+    EXPECT_FALSE(p2p_iface_->startWpsPinKeypad(kTestGroupIfName, kTestConnectPin).isOk());
+    EXPECT_FALSE(p2p_iface_->cancelWps(kTestGroupIfName).isOk());
+}
+
+/*
+ * Create message and report handover for NFC Request
+ */
+TEST_P(SupplicantP2pIfaceAidlTest, CreateAndReportNfcRequest) {
+    std::vector<uint8_t> requestMsg;
+    EXPECT_TRUE(p2p_iface_->createNfcHandoverRequestMessage(&requestMsg).isOk());
+    EXPECT_FALSE(requestMsg.empty());
+    EXPECT_TRUE(p2p_iface_->reportNfcHandoverResponse(requestMsg).isOk());
+}
+
+/*
+ * Create message and report handover for NFC Select
+ */
+TEST_P(SupplicantP2pIfaceAidlTest, CreateAndReportNfcSelect) {
+    std::vector<uint8_t> selectMsg;
+    EXPECT_TRUE(p2p_iface_->createNfcHandoverSelectMessage(&selectMsg).isOk());
+    EXPECT_FALSE(selectMsg.empty());
+    EXPECT_TRUE(p2p_iface_->reportNfcHandoverInitiation(selectMsg).isOk());
+}
+
+/*
+ * RemoveClient
+ */
+TEST_P(SupplicantP2pIfaceAidlTest, RemoveClient) {
+    // Method returns success for any valid MAC address
+    EXPECT_TRUE(p2p_iface_->removeClient(kTestMacAddr, false).isOk());
+    // Returns failure for any invalid MAC address
+    std::vector<uint8_t> invalidMacAddr = {0x11, 0x22};
+    EXPECT_FALSE(p2p_iface_->removeClient(invalidMacAddr, false).isOk());
+}
+
+/*
+ * ConfigureEapolIpAddressAllocationParams
+ */
+TEST_P(SupplicantP2pIfaceAidlTest, ConfigureEapolIpAddressAllocationParams) {
+    if (interface_version_ < 2) {
+        GTEST_SKIP() << "ConfigureEapolIpAddressAllocationParams is available as of Supplicant V2";
+    }
+    // The IP addresses are IPV4 addresses and higher-order address bytes are in the
+    // lower-order int bytes (e.g. 192.168.1.1 is represented as 0x0101A8C0)
+    EXPECT_TRUE(p2p_iface_
+                        ->configureEapolIpAddressAllocationParams(0x0101A8C0, 0x00FFFFFF,
+                                                                  0x0501A8C0, 0x0801A8C0)
+                        .isOk());
+
+    // Clear the configuration.
+    EXPECT_TRUE(p2p_iface_->configureEapolIpAddressAllocationParams(0, 0, 0, 0).isOk());
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantP2pIfaceAidlTest);
 INSTANTIATE_TEST_SUITE_P(Supplicant, SupplicantP2pIfaceAidlTest,
                          testing::ValuesIn(android::getAidlHalInstanceNames(
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_p2p_network_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_network_aidl_test.cpp
new file mode 100644
index 0000000..c5a73f1
--- /dev/null
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_network_aidl_test.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <VtsCoreUtil.h>
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/wifi/supplicant/BnSupplicant.h>
+#include <android/binder_manager.h>
+#include <android/binder_status.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cutils/properties.h>
+
+#include "supplicant_test_utils.h"
+#include "wifi_aidl_test_utils.h"
+
+using aidl::android::hardware::wifi::supplicant::DebugLevel;
+using aidl::android::hardware::wifi::supplicant::IfaceType;
+using aidl::android::hardware::wifi::supplicant::ISupplicantP2pNetwork;
+using aidl::android::hardware::wifi::supplicant::MacAddress;
+using android::ProcessState;
+
+class SupplicantP2pNetworkAidlTest : public testing::TestWithParam<std::string> {
+  public:
+    void SetUp() override {
+        initializeService();
+        supplicant_ = getSupplicant(GetParam().c_str());
+        ASSERT_NE(supplicant_, nullptr);
+        ASSERT_TRUE(supplicant_->setDebugParams(DebugLevel::EXCESSIVE, true, true).isOk());
+
+        bool p2pEnabled = testing::deviceSupportsFeature("android.hardware.wifi.direct");
+        if (!p2pEnabled) {
+            GTEST_SKIP() << "Wi-Fi Direct is not supported, skip this test.";
+        }
+
+        EXPECT_TRUE(supplicant_->getP2pInterface(getP2pIfaceName(), &p2p_iface_).isOk());
+        ASSERT_NE(p2p_iface_, nullptr);
+        EXPECT_TRUE(p2p_iface_->addNetwork(&p2p_network_).isOk());
+        ASSERT_NE(p2p_network_, nullptr);
+    }
+
+    void TearDown() override {
+        stopSupplicantService();
+        startWifiFramework();
+    }
+
+  protected:
+    std::shared_ptr<ISupplicant> supplicant_;
+    std::shared_ptr<ISupplicantP2pIface> p2p_iface_;
+    std::shared_ptr<ISupplicantP2pNetwork> p2p_network_;
+};
+
+/*
+ * GetBssid
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, GetBssid) {
+    std::vector<uint8_t> bssid;
+    EXPECT_TRUE(p2p_network_->getBssid(&bssid).isOk());
+}
+
+/*
+ * GetClientList
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, GetClientList) {
+    // Expect failure if there are no clients
+    std::vector<MacAddress> clientList;
+    EXPECT_FALSE(p2p_network_->getClientList(&clientList).isOk());
+}
+
+/*
+ * GetId
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, GetId) {
+    int networkId;
+    EXPECT_TRUE(p2p_network_->getId(&networkId).isOk());
+}
+
+/*
+ * GetInterfaceName
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, GetInterfaceName) {
+    std::string expectedName = getP2pIfaceName();
+    std::string retrievedName;
+    EXPECT_TRUE(p2p_network_->getInterfaceName(&retrievedName).isOk());
+    EXPECT_EQ(retrievedName, expectedName);
+}
+
+/*
+ * GetSsid
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, GetSsid) {
+    std::vector<uint8_t> ssid;
+    EXPECT_TRUE(p2p_network_->getSsid(&ssid).isOk());
+}
+
+/*
+ * GetType
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, GetType) {
+    IfaceType ifaceType;
+    EXPECT_TRUE(p2p_network_->getType(&ifaceType).isOk());
+    EXPECT_EQ(ifaceType, IfaceType::P2P);
+}
+
+/*
+ * IsCurrent
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, IsCurrent) {
+    bool isCurrent;
+    EXPECT_TRUE(p2p_network_->isCurrent(&isCurrent).isOk());
+    EXPECT_FALSE(isCurrent);
+}
+
+/*
+ * IsGroupOwner
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, IsGroupOwner) {
+    bool isGroupOwner;
+    EXPECT_TRUE(p2p_network_->isGroupOwner(&isGroupOwner).isOk());
+    EXPECT_FALSE(isGroupOwner);
+}
+
+/*
+ * IsPersistent
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, IsPersistent) {
+    bool isPersistent;
+    EXPECT_TRUE(p2p_network_->isPersistent(&isPersistent).isOk());
+    EXPECT_FALSE(isPersistent);
+}
+
+/*
+ * SetClientList
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, SetClientList) {
+    MacAddress client = {{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}};
+    std::vector clientList = {client};
+    EXPECT_TRUE(p2p_network_->setClientList(clientList).isOk());
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantP2pNetworkAidlTest);
+INSTANTIATE_TEST_SUITE_P(
+        Supplicant, SupplicantP2pNetworkAidlTest,
+        testing::ValuesIn(android::getAidlHalInstanceNames(ISupplicant::descriptor)),
+        android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ProcessState::self()->startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp
index c9ad498..257607f 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp
@@ -43,13 +43,20 @@
 using aidl::android::hardware::wifi::supplicant::ISupplicantStaIface;
 using aidl::android::hardware::wifi::supplicant::ISupplicantStaNetwork;
 using aidl::android::hardware::wifi::supplicant::KeyMgmtMask;
+using aidl::android::hardware::wifi::supplicant::MloLinksInfo;
 using aidl::android::hardware::wifi::supplicant::MscsParams;
 using aidl::android::hardware::wifi::supplicant::QosCharacteristics;
 using aidl::android::hardware::wifi::supplicant::QosPolicyScsData;
 using aidl::android::hardware::wifi::supplicant::QosPolicyScsRequestStatus;
+using aidl::android::hardware::wifi::supplicant::RxFilterType;
+using aidl::android::hardware::wifi::supplicant::SignalPollResult;
+using aidl::android::hardware::wifi::supplicant::UsdBaseConfig;
+using aidl::android::hardware::wifi::supplicant::UsdCapabilities;
 using aidl::android::hardware::wifi::supplicant::UsdMessageInfo;
-using aidl::android::hardware::wifi::supplicant::UsdReasonCode;
+using aidl::android::hardware::wifi::supplicant::UsdPublishConfig;
 using aidl::android::hardware::wifi::supplicant::UsdServiceDiscoveryInfo;
+using aidl::android::hardware::wifi::supplicant::UsdSubscribeConfig;
+using aidl::android::hardware::wifi::supplicant::UsdTerminateReasonCode;
 using aidl::android::hardware::wifi::supplicant::WpaDriverCapabilitiesMask;
 using aidl::android::hardware::wifi::supplicant::WpsConfigMethods;
 using android::ProcessState;
@@ -254,18 +261,20 @@
                                                int32_t /* subscribeId */) override {
         return ndk::ScopedAStatus::ok();
     }
-    ::ndk::ScopedAStatus onUsdPublishConfigFailed(int32_t /* cmdId */) override {
+    ::ndk::ScopedAStatus onUsdPublishConfigFailed(int32_t /* cmdId */,
+                                                  UsdConfigErrorCode /* errorCode */) override {
         return ndk::ScopedAStatus::ok();
     }
-    ::ndk::ScopedAStatus onUsdSubscribeConfigFailed(int32_t /* cmdId */) override {
+    ::ndk::ScopedAStatus onUsdSubscribeConfigFailed(int32_t /* cmdId */,
+                                                    UsdConfigErrorCode /* errorCode */) override {
         return ndk::ScopedAStatus::ok();
     }
     ::ndk::ScopedAStatus onUsdPublishTerminated(int32_t /* publishId */,
-                                                UsdReasonCode /* reasonCode */) override {
+                                                UsdTerminateReasonCode /* reasonCode */) override {
         return ndk::ScopedAStatus::ok();
     }
-    ::ndk::ScopedAStatus onUsdSubscribeTerminated(int32_t /* subscribeId */,
-                                                  UsdReasonCode /* reasonCode */) override {
+    ::ndk::ScopedAStatus onUsdSubscribeTerminated(
+            int32_t /* subscribeId */, UsdTerminateReasonCode /* reasonCode */) override {
         return ndk::ScopedAStatus::ok();
     }
     ::ndk::ScopedAStatus onUsdPublishReplied(const UsdServiceDiscoveryInfo& /* info */) override {
@@ -878,6 +887,183 @@
     EXPECT_EQ(1, responseList.size());
 }
 
+/*
+ * Verify that all USD methods check the Service Specific Info (SSI) length
+ * and fail if the provided SSI is too long.
+ */
+TEST_P(SupplicantStaIfaceAidlTest, InvalidUsdServiceSpecificInfo) {
+    if (interface_version_ < 4) {
+        GTEST_SKIP() << "USD is available as of Supplicant V4";
+    }
+
+    UsdCapabilities caps;
+    EXPECT_TRUE(sta_iface_->getUsdCapabilities(&caps).isOk());
+    if (!caps.isUsdPublisherSupported && !caps.isUsdSubscriberSupported) {
+        GTEST_SKIP() << "USD publish and subscribe are not supported";
+    }
+
+    int commandId = 123;
+    std::vector<uint8_t> invalidSsi(caps.maxLocalSsiLengthBytes + 1);
+    UsdBaseConfig invalidBaseConfig;
+    invalidBaseConfig.serviceSpecificInfo = invalidSsi;
+
+    if (caps.isUsdPublisherSupported) {
+        UsdPublishConfig publishConfig;
+        publishConfig.usdBaseConfig = invalidBaseConfig;
+        EXPECT_FALSE(sta_iface_->startUsdPublish(commandId, publishConfig).isOk());
+        EXPECT_FALSE(sta_iface_->updateUsdPublish(commandId, invalidSsi).isOk());
+    }
+
+    if (caps.isUsdSubscriberSupported) {
+        UsdSubscribeConfig subscribeConfig;
+        subscribeConfig.usdBaseConfig = invalidBaseConfig;
+        EXPECT_FALSE(sta_iface_->startUsdSubscribe(commandId, subscribeConfig).isOk());
+    }
+
+    UsdMessageInfo messageInfo;
+    messageInfo.message = invalidSsi;
+    EXPECT_FALSE(sta_iface_->sendUsdMessage(messageInfo).isOk());
+}
+
+/*
+ * Cancel a USD Publish and Subscribe session.
+ */
+TEST_P(SupplicantStaIfaceAidlTest, CancelUsdSession) {
+    if (interface_version_ < 4) {
+        GTEST_SKIP() << "USD is available as of Supplicant V4";
+    }
+
+    UsdCapabilities caps;
+    EXPECT_TRUE(sta_iface_->getUsdCapabilities(&caps).isOk());
+    if (!caps.isUsdPublisherSupported && !caps.isUsdSubscriberSupported) {
+        GTEST_SKIP() << "USD publish and subscribe are not supported";
+    }
+
+    int sessionId = 123;
+    if (caps.isUsdPublisherSupported) {
+        // Method is expected to succeed, even if the session does not exist.
+        EXPECT_TRUE(sta_iface_->cancelUsdPublish(sessionId).isOk());
+    }
+    if (caps.isUsdSubscriberSupported) {
+        EXPECT_TRUE(sta_iface_->cancelUsdSubscribe(sessionId).isOk());
+    }
+}
+
+/*
+ * GenerateSelfDppConfiguration
+ */
+TEST_P(SupplicantStaIfaceAidlTest, GenerateSelfDppConfiguration) {
+    if (!keyMgmtSupported(sta_iface_, KeyMgmtMask::DPP)) {
+        GTEST_SKIP() << "Missing DPP support";
+    }
+    const std::string ssid = "my_test_ssid";
+    const std::vector<uint8_t> eckey_in = {0x2, 0x3, 0x4};
+
+    // Expect to fail as this test requires a DPP AKM supported AP and a valid private EC
+    // key generated by wpa_supplicant.
+    EXPECT_FALSE(sta_iface_->generateSelfDppConfiguration(ssid, eckey_in).isOk());
+}
+
+/*
+ * getSignalPollResults
+ */
+TEST_P(SupplicantStaIfaceAidlTest, GetSignalPollResults) {
+    if (interface_version_ < 2) {
+        GTEST_SKIP() << "getSignalPollResults is available as of Supplicant V2";
+    }
+
+    std::vector<SignalPollResult> results;
+    EXPECT_TRUE(sta_iface_->getSignalPollResults(&results).isOk());
+}
+
+/*
+ * Test that we can add, remove, start, and stop an RX filter.
+ */
+TEST_P(SupplicantStaIfaceAidlTest, ConfigureRxFilter) {
+    RxFilterType filterType = RxFilterType::V4_MULTICAST;
+    EXPECT_TRUE(sta_iface_->addRxFilter(filterType).isOk());
+    EXPECT_TRUE(sta_iface_->startRxFilter().isOk());
+    EXPECT_TRUE(sta_iface_->stopRxFilter().isOk());
+    EXPECT_TRUE(sta_iface_->removeRxFilter(filterType).isOk());
+}
+
+/*
+ * Test that we can start and cancel all WPS methods.
+ */
+TEST_P(SupplicantStaIfaceAidlTest, StartAndCancelWps) {
+    std::vector<uint8_t> zeroMacAddr = {0, 0, 0, 0, 0, 0};
+    std::string pin = "12345678";
+
+    EXPECT_TRUE(sta_iface_->startWpsPbc(zeroMacAddr).isOk());
+    EXPECT_TRUE(sta_iface_->cancelWps().isOk());
+
+    std::string generatedPin;
+    EXPECT_TRUE(sta_iface_->startWpsPinDisplay(zeroMacAddr, &generatedPin).isOk());
+    EXPECT_TRUE(sta_iface_->cancelWps().isOk());
+
+    EXPECT_TRUE(sta_iface_->startWpsPinKeypad(pin).isOk());
+    EXPECT_TRUE(sta_iface_->cancelWps().isOk());
+
+    EXPECT_TRUE(sta_iface_->startWpsRegistrar(zeroMacAddr, pin).isOk());
+    EXPECT_TRUE(sta_iface_->cancelWps().isOk());
+}
+
+/*
+ * Test that we can add, list, get, and remove a network.
+ */
+TEST_P(SupplicantStaIfaceAidlTest, ManageNetwork) {
+    std::shared_ptr<ISupplicantStaNetwork> network;
+    EXPECT_TRUE(sta_iface_->addNetwork(&network).isOk());
+    EXPECT_NE(network, nullptr);
+
+    std::vector<int32_t> networkList;
+    EXPECT_TRUE(sta_iface_->listNetworks(&networkList).isOk());
+    EXPECT_EQ(networkList.size(), 1);
+
+    int networkId;
+    EXPECT_TRUE(network->getId(&networkId).isOk());
+    EXPECT_EQ(networkId, networkList[0]);
+
+    std::shared_ptr<ISupplicantStaNetwork> retrievedNetwork;
+    EXPECT_TRUE(sta_iface_->getNetwork(networkId, &retrievedNetwork).isOk());
+    EXPECT_NE(retrievedNetwork, nullptr);
+    EXPECT_TRUE(sta_iface_->removeNetwork(networkId).isOk());
+}
+
+/*
+ * EnableAutoReconnect
+ */
+TEST_P(SupplicantStaIfaceAidlTest, EnableAutoReconnect) {
+    EXPECT_TRUE(sta_iface_->enableAutoReconnect(true).isOk());
+    EXPECT_TRUE(sta_iface_->enableAutoReconnect(false).isOk());
+}
+
+/*
+ * SetQosPolicyFeatureEnabled
+ */
+TEST_P(SupplicantStaIfaceAidlTest, SetQosPolicyFeatureEnabled) {
+    EXPECT_TRUE(sta_iface_->setQosPolicyFeatureEnabled(true).isOk());
+    EXPECT_TRUE(sta_iface_->setQosPolicyFeatureEnabled(false).isOk());
+}
+
+/*
+ * GetConnectionMloLinksInfo
+ */
+TEST_P(SupplicantStaIfaceAidlTest, GetConnectionMloLinksInfo) {
+    MloLinksInfo mloInfo;
+    EXPECT_TRUE(sta_iface_->getConnectionMloLinksInfo(&mloInfo).isOk());
+}
+
+/*
+ * StopDppInitiator
+ */
+TEST_P(SupplicantStaIfaceAidlTest, StopDppInitiator) {
+    if (!keyMgmtSupported(sta_iface_, KeyMgmtMask::DPP)) {
+        GTEST_SKIP() << "Missing DPP support";
+    }
+    EXPECT_TRUE(sta_iface_->stopDppInitiator().isOk());
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantStaIfaceAidlTest);
 INSTANTIATE_TEST_SUITE_P(Supplicant, SupplicantStaIfaceAidlTest,
                          testing::ValuesIn(android::getAidlHalInstanceNames(
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp
index 9bdd2f5..5e6069f 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp
@@ -32,6 +32,7 @@
 using aidl::android::hardware::wifi::supplicant::AuthAlgMask;
 using aidl::android::hardware::wifi::supplicant::BnSupplicantStaNetworkCallback;
 using aidl::android::hardware::wifi::supplicant::DebugLevel;
+using aidl::android::hardware::wifi::supplicant::DppConnectionKeys;
 using aidl::android::hardware::wifi::supplicant::EapMethod;
 using aidl::android::hardware::wifi::supplicant::EapPhase2Method;
 using aidl::android::hardware::wifi::supplicant::GroupCipherMask;
@@ -837,6 +838,21 @@
 }
 
 /*
+ * SetDppKeys
+ */
+TEST_P(SupplicantStaNetworkAidlTest, SetDppKeys) {
+    if (!keyMgmtSupported(sta_iface_, KeyMgmtMask::DPP)) {
+        GTEST_SKIP() << "Missing DPP support";
+    }
+
+    DppConnectionKeys in_keys;
+    in_keys.connector = std::vector<uint8_t>({0x11, 0x22, 0x33, 0x44});
+    in_keys.cSign = std::vector<uint8_t>({0x55, 0x66, 0x77, 0x88});
+    in_keys.netAccessKey = std::vector<uint8_t>({0xaa, 0xbb, 0xcc, 0xdd});
+    EXPECT_TRUE(sta_network_->setDppKeys(in_keys).isOk());
+}
+
+/*
  * SetVendorData
  */
 TEST_P(SupplicantStaNetworkAidlTest, SetVendorData) {