Merge "We are not deprecating nvResetConfig" into main
diff --git a/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv b/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
index 56b7926..e2da90d 100644
--- a/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
+++ b/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
@@ -45,6 +45,8 @@
     PAUSED -> ACTIVE [label="start"];                 // consumer -> active
     PAUSED -> IDLE [label="flush"];                   // producer -> passive, buffer is cleared
     DRAINING -> IDLE [label="←IStreamCallback.onDrainReady"];
+    DRAINING -> DRAINING [label="←IStreamCallback.onDrainReady"];  // allowed for `DRAIN_EARLY_NOTIFY`
+    DRAINING -> IDLE [label="<empty buffer>"];        // allowed for `DRAIN_EARLY_NOTIFY`
     DRAINING -> TRANSFERRING [label="burst"];         // producer -> active
     DRAINING -> ACTIVE [label="burst"];               // full write
     DRAINING -> DRAIN_PAUSED [label="pause"];         // consumer -> passive (not consuming)
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 51b6085..e96cf81 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -207,9 +207,9 @@
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
     const auto& flags = portConfigIt->flags.value();
-    StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
-                                          mVendorDebug.forceTransientBurst,
-                                          mVendorDebug.forceSynchronousDrain};
+    StreamContext::DebugParameters params{
+            mDebug.streamTransientStateDelayMs, mVendorDebug.forceTransientBurst,
+            mVendorDebug.forceSynchronousDrain, mVendorDebug.forceDrainToDraining};
     std::unique_ptr<StreamContext::DataMQ> dataMQ = nullptr;
     std::shared_ptr<IStreamCallback> streamAsyncCallback = nullptr;
     std::shared_ptr<ISoundDose> soundDose;
@@ -1524,6 +1524,7 @@
 
 const std::string Module::VendorDebug::kForceTransientBurstName = "aosp.forceTransientBurst";
 const std::string Module::VendorDebug::kForceSynchronousDrainName = "aosp.forceSynchronousDrain";
+const std::string Module::VendorDebug::kForceDrainToDrainingName = "aosp.forceDrainToDraining";
 
 ndk::ScopedAStatus Module::getVendorParameters(const std::vector<std::string>& in_ids,
                                                std::vector<VendorParameter>* _aidl_return) {
@@ -1538,6 +1539,10 @@
             VendorParameter forceSynchronousDrain{.id = id};
             forceSynchronousDrain.ext.setParcelable(Boolean{mVendorDebug.forceSynchronousDrain});
             _aidl_return->push_back(std::move(forceSynchronousDrain));
+        } else if (id == VendorDebug::kForceDrainToDrainingName) {
+            VendorParameter forceDrainToDraining{.id = id};
+            forceDrainToDraining.ext.setParcelable(Boolean{mVendorDebug.forceDrainToDraining});
+            _aidl_return->push_back(std::move(forceDrainToDraining));
         } else {
             allParametersKnown = false;
             LOG(VERBOSE) << __func__ << ": " << mType << ": unrecognized parameter \"" << id << "\"";
@@ -1578,6 +1583,10 @@
             if (!extractParameter<Boolean>(p, &mVendorDebug.forceSynchronousDrain)) {
                 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
             }
+        } else if (p.id == VendorDebug::kForceDrainToDrainingName) {
+            if (!extractParameter<Boolean>(p, &mVendorDebug.forceDrainToDraining)) {
+                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+            }
         } else {
             allParametersKnown = false;
             LOG(VERBOSE) << __func__ << ": " << mType << ": unrecognized parameter \"" << p.id
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 3d7f30c..4525f6a 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -382,8 +382,20 @@
 const std::string StreamOutWorkerLogic::kThreadName = "writer";
 
 StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
-    if (mState == StreamDescriptor::State::DRAINING ||
-        mState == StreamDescriptor::State::TRANSFERRING) {
+    if (mState == StreamDescriptor::State::DRAINING && mContext->getForceDrainToDraining() &&
+        mOnDrainReadyStatus == OnDrainReadyStatus::UNSENT) {
+        std::shared_ptr<IStreamCallback> asyncCallback = mContext->getAsyncCallback();
+        if (asyncCallback != nullptr) {
+            ndk::ScopedAStatus status = asyncCallback->onDrainReady();
+            if (!status.isOk()) {
+                LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
+            }
+            // This sets the timeout for moving into IDLE on next iterations.
+            switchToTransientState(StreamDescriptor::State::DRAINING);
+            mOnDrainReadyStatus = OnDrainReadyStatus::SENT;
+        }
+    } else if (mState == StreamDescriptor::State::DRAINING ||
+               mState == StreamDescriptor::State::TRANSFERRING) {
         if (auto stateDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(
                     std::chrono::steady_clock::now() - mTransientStateStart);
             stateDurationMs >= mTransientStateDelayMs) {
@@ -396,9 +408,12 @@
                 // drain or transfer completion. In the stub, we switch unconditionally.
                 if (mState == StreamDescriptor::State::DRAINING) {
                     mState = StreamDescriptor::State::IDLE;
-                    ndk::ScopedAStatus status = asyncCallback->onDrainReady();
-                    if (!status.isOk()) {
-                        LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
+                    if (mOnDrainReadyStatus != OnDrainReadyStatus::SENT) {
+                        ndk::ScopedAStatus status = asyncCallback->onDrainReady();
+                        if (!status.isOk()) {
+                            LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
+                        }
+                        mOnDrainReadyStatus = OnDrainReadyStatus::SENT;
                     }
                 } else {
                     mState = StreamDescriptor::State::ACTIVE;
@@ -537,6 +552,10 @@
                             mState = StreamDescriptor::State::IDLE;
                         } else {
                             switchToTransientState(StreamDescriptor::State::DRAINING);
+                            mOnDrainReadyStatus =
+                                    mode == StreamDescriptor::DrainMode::DRAIN_EARLY_NOTIFY
+                                            ? OnDrainReadyStatus::UNSENT
+                                            : OnDrainReadyStatus::IGNORE;
                         }
                     } else {
                         LOG(ERROR) << __func__ << ": drain failed: " << status;
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 7e32cf2..d03598a 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -148,8 +148,10 @@
     struct VendorDebug {
         static const std::string kForceTransientBurstName;
         static const std::string kForceSynchronousDrainName;
+        static const std::string kForceDrainToDrainingName;
         bool forceTransientBurst = false;
         bool forceSynchronousDrain = false;
+        bool forceDrainToDraining = false;
     };
     // ids of device ports created at runtime via 'connectExternalDevice'.
     // Also stores a list of ids of mix ports with dynamic profiles that were populated from
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index f7b9269..8297fc5 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -78,6 +78,10 @@
         bool forceTransientBurst = false;
         // Force the "drain" command to be synchronous, going directly to the IDLE state.
         bool forceSynchronousDrain = false;
+        // Force the "drain early notify" command to keep the SM in the DRAINING state
+        // after sending 'onDrainReady' callback. The SM moves to IDLE after
+        // 'transientStateDelayMs'.
+        bool forceDrainToDraining = false;
     };
 
     StreamContext() = default;
@@ -119,6 +123,7 @@
     ::aidl::android::media::audio::common::AudioIoFlags getFlags() const { return mFlags; }
     bool getForceTransientBurst() const { return mDebugParameters.forceTransientBurst; }
     bool getForceSynchronousDrain() const { return mDebugParameters.forceSynchronousDrain; }
+    bool getForceDrainToDraining() const { return mDebugParameters.forceDrainToDraining; }
     size_t getFrameSize() const;
     int getInternalCommandCookie() const { return mInternalCommandCookie; }
     int32_t getMixPortHandle() const { return mMixPortHandle; }
@@ -301,6 +306,9 @@
     bool write(size_t clientSize, StreamDescriptor::Reply* reply);
 
     std::shared_ptr<IStreamOutEventCallback> mEventCallback;
+
+    enum OnDrainReadyStatus : int32_t { IGNORE /*used for DRAIN_ALL*/, UNSENT, SENT };
+    OnDrainReadyStatus mOnDrainReadyStatus = OnDrainReadyStatus::IGNORE;
 };
 using StreamOutWorker = StreamWorkerImpl<StreamOutWorkerLogic>;
 
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index 6bfba65..6bce107 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -117,6 +117,10 @@
 using ndk::enum_range;
 using ndk::ScopedAStatus;
 
+static constexpr int32_t kAidlVersion1 = 1;
+static constexpr int32_t kAidlVersion2 = 2;
+static constexpr int32_t kAidlVersion3 = 3;
+
 template <typename T>
 std::set<int32_t> extractIds(const std::vector<T>& v) {
     std::set<int32_t> ids;
@@ -452,7 +456,6 @@
     // This is implemented by the 'StreamFixture' utility class.
     static constexpr int kNegativeTestBufferSizeFrames = 256;
     static constexpr int kDefaultLargeBufferSizeFrames = 48000;
-    static constexpr int32_t kAidlVersion3 = 3;
 
     void SetUpImpl(const std::string& moduleName, bool setUpDebug = true) {
         ASSERT_NO_FATAL_FAILURE(ConnectToService(moduleName, setUpDebug));
@@ -582,7 +585,7 @@
     std::unique_ptr<WithDebugFlags> debug;
     std::vector<AudioPort> initialPorts;
     std::vector<AudioRoute> initialRoutes;
-    int32_t aidlVersion;
+    int32_t aidlVersion = -1;
 };
 
 class WithDevicePortConnectedState {
@@ -1837,6 +1840,7 @@
 }
 
 TEST_P(AudioCoreModule, SetAudioPortConfigInvalidPortAudioGain) {
+    ASSERT_GE(aidlVersion, kAidlVersion1);
     if (aidlVersion < kAidlVersion3) {
         GTEST_SKIP() << "Skip for audio HAL version lower than " << kAidlVersion3;
     }
@@ -4021,6 +4025,7 @@
 
 enum {
     NAMED_CMD_NAME,
+    NAMED_CMD_MIN_INTERFACE_VERSION,
     NAMED_CMD_DELAY_MS,
     NAMED_CMD_STREAM_TYPE,
     NAMED_CMD_CMDS,
@@ -4028,7 +4033,7 @@
 };
 enum class StreamTypeFilter { ANY, SYNC, ASYNC };
 using NamedCommandSequence =
-        std::tuple<std::string, int /*cmdDelayMs*/, StreamTypeFilter,
+        std::tuple<std::string, int /*minInterfaceVersion*/, int /*cmdDelayMs*/, StreamTypeFilter,
                    std::shared_ptr<StateSequence>, bool /*validatePositionIncrease*/>;
 enum { PARAM_MODULE_NAME, PARAM_CMD_SEQ, PARAM_SETUP_SEQ };
 using StreamIoTestParameters =
@@ -4039,6 +4044,12 @@
   public:
     void SetUp() override {
         ASSERT_NO_FATAL_FAILURE(SetUpImpl(std::get<PARAM_MODULE_NAME>(GetParam())));
+        ASSERT_GE(aidlVersion, kAidlVersion1);
+        if (const int minVersion =
+                    std::get<NAMED_CMD_MIN_INTERFACE_VERSION>(std::get<PARAM_CMD_SEQ>(GetParam()));
+            aidlVersion < minVersion) {
+            GTEST_SKIP() << "Skip for audio HAL version lower than " << minVersion;
+        }
         ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
     }
 
@@ -4048,6 +4059,20 @@
         if (allPortConfigs.empty()) {
             GTEST_SKIP() << "No mix ports have attached devices";
         }
+        const auto& commandsAndStates =
+                std::get<NAMED_CMD_CMDS>(std::get<PARAM_CMD_SEQ>(GetParam()));
+        const bool validatePositionIncrease =
+                std::get<NAMED_CMD_VALIDATE_POS_INCREASE>(std::get<PARAM_CMD_SEQ>(GetParam()));
+        auto runStreamIoCommands = [&](const AudioPortConfig& portConfig) {
+            if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
+                ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(portConfig, commandsAndStates,
+                                                                    validatePositionIncrease));
+            } else {
+                ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq2(portConfig, commandsAndStates,
+                                                                    validatePositionIncrease));
+            }
+        };
+
         for (const auto& portConfig : allPortConfigs) {
             auto port = moduleConfig->getPort(portConfig.portId);
             ASSERT_TRUE(port.has_value());
@@ -4075,16 +4100,18 @@
             delayTransientStates.flags().streamTransientStateDelayMs =
                     std::get<NAMED_CMD_DELAY_MS>(std::get<PARAM_CMD_SEQ>(GetParam()));
             ASSERT_NO_FATAL_FAILURE(delayTransientStates.SetUp(module.get()));
-            const auto& commandsAndStates =
-                    std::get<NAMED_CMD_CMDS>(std::get<PARAM_CMD_SEQ>(GetParam()));
-            const bool validatePositionIncrease =
-                    std::get<NAMED_CMD_VALIDATE_POS_INCREASE>(std::get<PARAM_CMD_SEQ>(GetParam()));
-            if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
-                ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(portConfig, commandsAndStates,
-                                                                    validatePositionIncrease));
-            } else {
-                ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq2(portConfig, commandsAndStates,
-                                                                    validatePositionIncrease));
+            ASSERT_NO_FATAL_FAILURE(runStreamIoCommands(portConfig));
+            if (aidlVersion >= kAidlVersion3 && isNonBlocking && !IOTraits<Stream>::is_input) {
+                // Also try running the same sequence with "aosp.forceDrainToDraining" set.
+                // This will only work with the default implementation. When it works, the stream
+                // tries always to move to the 'DRAINING' state after an "early notify" drain.
+                // This helps to check more paths for our test scenarios.
+                WithModuleParameter forceDrainToDraining("aosp.forceDrainToDraining",
+                                                         Boolean{true});
+                if (forceDrainToDraining.SetUpNoChecks(module.get(), true /*failureExpected*/)
+                            .isOk()) {
+                    ASSERT_NO_FATAL_FAILURE(runStreamIoCommands(portConfig));
+                }
             }
             if (isNonBlocking) {
                 // Also try running the same sequence with "aosp.forceTransientBurst" set.
@@ -4094,13 +4121,7 @@
                 WithModuleParameter forceTransientBurst("aosp.forceTransientBurst", Boolean{true});
                 if (forceTransientBurst.SetUpNoChecks(module.get(), true /*failureExpected*/)
                             .isOk()) {
-                    if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
-                        ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(
-                                portConfig, commandsAndStates, validatePositionIncrease));
-                    } else {
-                        ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq2(
-                                portConfig, commandsAndStates, validatePositionIncrease));
-                    }
+                    ASSERT_NO_FATAL_FAILURE(runStreamIoCommands(portConfig));
                 }
             } else if (!IOTraits<Stream>::is_input) {
                 // Also try running the same sequence with "aosp.forceSynchronousDrain" set.
@@ -4111,13 +4132,7 @@
                                                           Boolean{true});
                 if (forceSynchronousDrain.SetUpNoChecks(module.get(), true /*failureExpected*/)
                             .isOk()) {
-                    if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
-                        ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(
-                                portConfig, commandsAndStates, validatePositionIncrease));
-                    } else {
-                        ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq2(
-                                portConfig, commandsAndStates, validatePositionIncrease));
-                    }
+                    ASSERT_NO_FATAL_FAILURE(runStreamIoCommands(portConfig));
                 }
             }
         }
@@ -4570,14 +4585,14 @@
     return std::make_shared<StateSequenceFollower>(std::move(d));
 }
 static const NamedCommandSequence kReadSeq =
-        std::make_tuple(std::string("Read"), 0, StreamTypeFilter::ANY, makeBurstCommands(true),
-                        true /*validatePositionIncrease*/);
+        std::make_tuple(std::string("Read"), kAidlVersion1, 0, StreamTypeFilter::ANY,
+                        makeBurstCommands(true), true /*validatePositionIncrease*/);
 static const NamedCommandSequence kWriteSyncSeq =
-        std::make_tuple(std::string("Write"), 0, StreamTypeFilter::SYNC, makeBurstCommands(true),
-                        true /*validatePositionIncrease*/);
+        std::make_tuple(std::string("Write"), kAidlVersion1, 0, StreamTypeFilter::SYNC,
+                        makeBurstCommands(true), true /*validatePositionIncrease*/);
 static const NamedCommandSequence kWriteAsyncSeq =
-        std::make_tuple(std::string("Write"), 0, StreamTypeFilter::ASYNC, makeBurstCommands(false),
-                        true /*validatePositionIncrease*/);
+        std::make_tuple(std::string("Write"), kAidlVersion1, 0, StreamTypeFilter::ASYNC,
+                        makeBurstCommands(false), true /*validatePositionIncrease*/);
 
 std::shared_ptr<StateSequence> makeAsyncDrainCommands(bool isInput) {
     using State = StreamDescriptor::State;
@@ -4606,10 +4621,10 @@
     return std::make_shared<StateSequenceFollower>(std::move(d));
 }
 static const NamedCommandSequence kWriteDrainAsyncSeq = std::make_tuple(
-        std::string("WriteDrain"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
-        makeAsyncDrainCommands(false), false /*validatePositionIncrease*/);
+        std::string("WriteDrain"), kAidlVersion1, kStreamTransientStateTransitionDelayMs,
+        StreamTypeFilter::ASYNC, makeAsyncDrainCommands(false), false /*validatePositionIncrease*/);
 static const NamedCommandSequence kDrainInSeq =
-        std::make_tuple(std::string("Drain"), 0, StreamTypeFilter::ANY,
+        std::make_tuple(std::string("Drain"), kAidlVersion1, 0, StreamTypeFilter::ANY,
                         makeAsyncDrainCommands(true), false /*validatePositionIncrease*/);
 
 std::shared_ptr<StateSequence> makeDrainOutCommands(bool isSync) {
@@ -4631,12 +4646,28 @@
     return std::make_shared<StateSequenceFollower>(std::move(d));
 }
 static const NamedCommandSequence kDrainOutSyncSeq =
-        std::make_tuple(std::string("Drain"), 0, StreamTypeFilter::SYNC, makeDrainOutCommands(true),
-                        false /*validatePositionIncrease*/);
+        std::make_tuple(std::string("Drain"), kAidlVersion1, 0, StreamTypeFilter::SYNC,
+                        makeDrainOutCommands(true), false /*validatePositionIncrease*/);
 static const NamedCommandSequence kDrainOutAsyncSeq =
-        std::make_tuple(std::string("Drain"), 0, StreamTypeFilter::ASYNC,
+        std::make_tuple(std::string("Drain"), kAidlVersion3, 0, StreamTypeFilter::ASYNC,
                         makeDrainOutCommands(false), false /*validatePositionIncrease*/);
 
+std::shared_ptr<StateSequence> makeDrainEarlyOutCommands() {
+    using State = StreamDescriptor::State;
+    auto d = std::make_unique<StateDag>();
+    StateDag::Node last = d->makeFinalNode(State::IDLE);
+    StateDag::Node draining = d->makeNode(State::DRAINING, kDrainReadyEvent, last);
+    draining.children().push_back(d->makeNode(State::DRAINING, kGetStatusCommand, last));
+    StateDag::Node active = d->makeNode(State::ACTIVE, kDrainOutEarlyCommand, draining);
+    StateDag::Node idle = d->makeNode(State::IDLE, kBurstCommand, active);
+    idle.children().push_back(d->makeNode(State::TRANSFERRING, kTransferReadyEvent, active));
+    d->makeNode(State::STANDBY, kStartCommand, idle);
+    return std::make_shared<StateSequenceFollower>(std::move(d));
+}
+static const NamedCommandSequence kDrainEarlyOutAsyncSeq =
+        std::make_tuple(std::string("DrainEarly"), kAidlVersion3, 0, StreamTypeFilter::ASYNC,
+                        makeDrainEarlyOutCommands(), false /*validatePositionIncrease*/);
+
 std::shared_ptr<StateSequence> makeDrainPauseOutCommands(bool isSync) {
     using State = StreamDescriptor::State;
     auto d = std::make_unique<StateDag>();
@@ -4656,12 +4687,33 @@
     d->makeNode(State::STANDBY, kStartCommand, idle);
     return std::make_shared<StateSequenceFollower>(std::move(d));
 }
-static const NamedCommandSequence kDrainPauseOutSyncSeq = std::make_tuple(
-        std::string("DrainPause"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::SYNC,
-        makeDrainPauseOutCommands(true), false /*validatePositionIncrease*/);
-static const NamedCommandSequence kDrainPauseOutAsyncSeq = std::make_tuple(
-        std::string("DrainPause"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
-        makeDrainPauseOutCommands(false), false /*validatePositionIncrease*/);
+static const NamedCommandSequence kDrainPauseOutSyncSeq =
+        std::make_tuple(std::string("DrainPause"), kAidlVersion1,
+                        kStreamTransientStateTransitionDelayMs, StreamTypeFilter::SYNC,
+                        makeDrainPauseOutCommands(true), false /*validatePositionIncrease*/);
+static const NamedCommandSequence kDrainPauseOutAsyncSeq =
+        std::make_tuple(std::string("DrainPause"), kAidlVersion1,
+                        kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
+                        makeDrainPauseOutCommands(false), false /*validatePositionIncrease*/);
+
+std::shared_ptr<StateSequence> makeDrainEarlyPauseOutCommands() {
+    using State = StreamDescriptor::State;
+    auto d = std::make_unique<StateDag>();
+    StateDag::Node draining = d->makeNodes({std::make_pair(State::DRAINING, kPauseCommand),
+                                            std::make_pair(State::DRAIN_PAUSED, kStartCommand),
+                                            std::make_pair(State::DRAINING, kPauseCommand),
+                                            std::make_pair(State::DRAIN_PAUSED, kBurstCommand)},
+                                           State::TRANSFER_PAUSED);
+    StateDag::Node active = d->makeNode(State::ACTIVE, kDrainOutEarlyCommand, draining);
+    StateDag::Node idle = d->makeNode(State::IDLE, kBurstCommand, active);
+    idle.children().push_back(d->makeNode(State::TRANSFERRING, kDrainOutEarlyCommand, draining));
+    d->makeNode(State::STANDBY, kStartCommand, idle);
+    return std::make_shared<StateSequenceFollower>(std::move(d));
+}
+static const NamedCommandSequence kDrainEarlyPauseOutAsyncSeq =
+        std::make_tuple(std::string("DrainEarlyPause"), kAidlVersion3,
+                        kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
+                        makeDrainEarlyPauseOutCommands(), false /*validatePositionIncrease*/);
 
 // This sequence also verifies that the capture / presentation position is not reset on standby.
 std::shared_ptr<StateSequence> makeStandbyCommands(bool isInput, bool isSync) {
@@ -4703,14 +4755,15 @@
     return std::make_shared<StateSequenceFollower>(std::move(d));
 }
 static const NamedCommandSequence kStandbyInSeq =
-        std::make_tuple(std::string("Standby"), 0, StreamTypeFilter::ANY,
+        std::make_tuple(std::string("Standby"), kAidlVersion1, 0, StreamTypeFilter::ANY,
                         makeStandbyCommands(true, false), false /*validatePositionIncrease*/);
 static const NamedCommandSequence kStandbyOutSyncSeq =
-        std::make_tuple(std::string("Standby"), 0, StreamTypeFilter::SYNC,
+        std::make_tuple(std::string("Standby"), kAidlVersion1, 0, StreamTypeFilter::SYNC,
                         makeStandbyCommands(false, true), false /*validatePositionIncrease*/);
-static const NamedCommandSequence kStandbyOutAsyncSeq = std::make_tuple(
-        std::string("Standby"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
-        makeStandbyCommands(false, false), false /*validatePositionIncrease*/);
+static const NamedCommandSequence kStandbyOutAsyncSeq =
+        std::make_tuple(std::string("Standby"), kAidlVersion1,
+                        kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
+                        makeStandbyCommands(false, false), false /*validatePositionIncrease*/);
 
 std::shared_ptr<StateSequence> makePauseCommands(bool isInput, bool isSync) {
     using State = StreamDescriptor::State;
@@ -4745,14 +4798,15 @@
     return std::make_shared<StateSequenceFollower>(std::move(d));
 }
 static const NamedCommandSequence kPauseInSeq =
-        std::make_tuple(std::string("Pause"), 0, StreamTypeFilter::ANY,
+        std::make_tuple(std::string("Pause"), kAidlVersion1, 0, StreamTypeFilter::ANY,
                         makePauseCommands(true, false), false /*validatePositionIncrease*/);
 static const NamedCommandSequence kPauseOutSyncSeq =
-        std::make_tuple(std::string("Pause"), 0, StreamTypeFilter::SYNC,
+        std::make_tuple(std::string("Pause"), kAidlVersion1, 0, StreamTypeFilter::SYNC,
                         makePauseCommands(false, true), false /*validatePositionIncrease*/);
-static const NamedCommandSequence kPauseOutAsyncSeq = std::make_tuple(
-        std::string("Pause"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
-        makePauseCommands(false, false), false /*validatePositionIncrease*/);
+static const NamedCommandSequence kPauseOutAsyncSeq =
+        std::make_tuple(std::string("Pause"), kAidlVersion1, kStreamTransientStateTransitionDelayMs,
+                        StreamTypeFilter::ASYNC, makePauseCommands(false, false),
+                        false /*validatePositionIncrease*/);
 
 std::shared_ptr<StateSequence> makeFlushCommands(bool isInput, bool isSync) {
     using State = StreamDescriptor::State;
@@ -4780,14 +4834,15 @@
     return std::make_shared<StateSequenceFollower>(std::move(d));
 }
 static const NamedCommandSequence kFlushInSeq =
-        std::make_tuple(std::string("Flush"), 0, StreamTypeFilter::ANY,
+        std::make_tuple(std::string("Flush"), kAidlVersion1, 0, StreamTypeFilter::ANY,
                         makeFlushCommands(true, false), false /*validatePositionIncrease*/);
 static const NamedCommandSequence kFlushOutSyncSeq =
-        std::make_tuple(std::string("Flush"), 0, StreamTypeFilter::SYNC,
+        std::make_tuple(std::string("Flush"), kAidlVersion1, 0, StreamTypeFilter::SYNC,
                         makeFlushCommands(false, true), false /*validatePositionIncrease*/);
-static const NamedCommandSequence kFlushOutAsyncSeq = std::make_tuple(
-        std::string("Flush"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
-        makeFlushCommands(false, false), false /*validatePositionIncrease*/);
+static const NamedCommandSequence kFlushOutAsyncSeq =
+        std::make_tuple(std::string("Flush"), kAidlVersion1, kStreamTransientStateTransitionDelayMs,
+                        StreamTypeFilter::ASYNC, makeFlushCommands(false, false),
+                        false /*validatePositionIncrease*/);
 
 std::shared_ptr<StateSequence> makeDrainPauseFlushOutCommands(bool isSync) {
     using State = StreamDescriptor::State;
@@ -4807,13 +4862,13 @@
     return std::make_shared<StateSequenceFollower>(std::move(d));
 }
 static const NamedCommandSequence kDrainPauseFlushOutSyncSeq =
-        std::make_tuple(std::string("DrainPauseFlush"), kStreamTransientStateTransitionDelayMs,
-                        StreamTypeFilter::SYNC, makeDrainPauseFlushOutCommands(true),
-                        false /*validatePositionIncrease*/);
+        std::make_tuple(std::string("DrainPauseFlush"), kAidlVersion1,
+                        kStreamTransientStateTransitionDelayMs, StreamTypeFilter::SYNC,
+                        makeDrainPauseFlushOutCommands(true), false /*validatePositionIncrease*/);
 static const NamedCommandSequence kDrainPauseFlushOutAsyncSeq =
-        std::make_tuple(std::string("DrainPauseFlush"), kStreamTransientStateTransitionDelayMs,
-                        StreamTypeFilter::ASYNC, makeDrainPauseFlushOutCommands(false),
-                        false /*validatePositionIncrease*/);
+        std::make_tuple(std::string("DrainPauseFlush"), kAidlVersion1,
+                        kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
+                        makeDrainPauseFlushOutCommands(false), false /*validatePositionIncrease*/);
 
 // Note, this isn't the "official" enum printer, it is only used to make the test name suffix.
 std::string PrintStreamFilterToString(StreamTypeFilter filter) {
@@ -4851,9 +4906,10 @@
         AudioStreamIoOutTest, AudioStreamIoOut,
         testing::Combine(testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
                          testing::Values(kWriteSyncSeq, kWriteAsyncSeq, kWriteDrainAsyncSeq,
-                                         kDrainOutSyncSeq, kDrainPauseOutSyncSeq,
-                                         kDrainPauseOutAsyncSeq, kStandbyOutSyncSeq,
-                                         kStandbyOutAsyncSeq,
+                                         kDrainOutSyncSeq, kDrainOutAsyncSeq,
+                                         kDrainEarlyOutAsyncSeq, kDrainPauseOutSyncSeq,
+                                         kDrainPauseOutAsyncSeq, kDrainEarlyPauseOutAsyncSeq,
+                                         kStandbyOutSyncSeq, kStandbyOutAsyncSeq,
                                          kPauseOutSyncSeq,  // kPauseOutAsyncSeq,
                                          kFlushOutSyncSeq, kFlushOutAsyncSeq,
                                          kDrainPauseFlushOutSyncSeq, kDrainPauseFlushOutAsyncSeq),
diff --git a/automotive/TEST_MAPPING b/automotive/TEST_MAPPING
index 483a85f..2b2f6b0 100644
--- a/automotive/TEST_MAPPING
+++ b/automotive/TEST_MAPPING
@@ -64,7 +64,82 @@
       "name": "CarServiceTelemetryTest"
     },
     {
-      "name": "CarServiceUnitTest"
+      "name": "CarServiceCarUnitTest"
+    },
+    {
+      "name": "CarServiceWifiUnitTest"
+    },
+    {
+      "name": "CarServiceWatchdogUnitTest"
+    },
+    {
+      "name": "CarServiceVmsUnitTest"
+    },
+    {
+      "name": "CarServiceUtilUnitTest"
+    },
+    {
+      "name": "CarServiceUserUnitTest"
+    },
+    {
+      "name": "CarServiceTelemetryUnitTest"
+    },
+    {
+      "name": "CarServiceSystemUiUnitTest"
+    },
+    {
+      "name": "CarServiceSystemInterfaceUnitTest"
+    },
+    {
+      "name": "CarServiceStorageMonitoringUnitTest"
+    },
+    {
+      "name": "CarServiceStatsUnitTest"
+    },
+    {
+      "name": "CarServiceRemoteAccessUnitTest"
+    },
+    {
+      "name": "CarServicePropertyUnitTest"
+    },
+    {
+      "name": "CarServicePowerUnitTest"
+    },
+    {
+      "name": "CarServicePmUnitTest"
+    },
+    {
+      "name": "CarServiceOsUnitTest"
+    },
+    {
+      "name": "CarServiceOemUnitTest"
+    },
+    {
+      "name": "CarServiceOccupantConnectionUnitTest"
+    },
+    {
+      "name": "CarServiceHalUnitTest"
+    },
+    {
+      "name": "CarServiceGarageModeUnitTest"
+    },
+    {
+      "name": "CarServiceEvsUnitTest"
+    },
+    {
+      "name": "CarServiceClusterUnitTest"
+    },
+    {
+      "name": "CarServiceBluetoothUnitTest"
+    },
+    {
+      "name": "CarServiceAudioUnitTest"
+    },
+    {
+      "name": "CarServiceAmUnitTest"
+    },
+    {
+      "name": "CarServiceAdminUnitTest"
     },
     {
       "name": "CarServiceVmsTest"
diff --git a/automotive/can/1.0/default/libnetdevice/Android.bp b/automotive/can/1.0/default/libnetdevice/Android.bp
index affbeee..4131a65 100644
--- a/automotive/can/1.0/default/libnetdevice/Android.bp
+++ b/automotive/can/1.0/default/libnetdevice/Android.bp
@@ -23,11 +23,20 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
-cc_library_static {
-    name: "android.hardware.automotive.can@libnetdevice",
-    defaults: ["android.hardware.automotive.can@defaults"],
+cc_defaults {
+    name: "libnetdevice-common",
     host_supported: true,
     vendor_available: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+    ],
+    shared_libs: [
+        "libbase",
+        "libutils",
+    ],
     srcs: [
         "can.cpp",
         "common.cpp",
@@ -41,3 +50,14 @@
         "libnl++",
     ],
 }
+
+// TODO: migrate to "libnetdevice" and remove
+cc_library_static {
+    name: "android.hardware.automotive.can@libnetdevice",
+    defaults: ["libnetdevice-common"],
+}
+
+cc_library_static {
+    name: "libnetdevice",
+    defaults: ["libnetdevice-common"],
+}
diff --git a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
index 75655d5..15ff491 100644
--- a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
+++ b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
@@ -128,7 +128,8 @@
  * \param addr IPv4 address to set
  * \return true in case of success, false otherwise
  */
-bool setAddr4(std::string_view ifname, std::string_view addr);
+bool setAddr4(std::string_view ifname, std::string_view addr,
+              std::optional<uint8_t> prefixlen = std::nullopt);
 
 /**
  * Add new IPv4 address to a given interface.
diff --git a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
index 1830633..9bb1a57 100644
--- a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
+++ b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
@@ -93,14 +93,31 @@
     return addrn;
 }
 
-bool setAddr4(std::string_view ifname, std::string_view addr) {
-    auto ifr = ifreqs::fromName(ifname);
+static in_addr_t prefixLengthToIpv4Netmask(uint8_t prefixlen) {
+    in_addr_t zero = 0;
+    return htonl(~zero << (32 - prefixlen));
+}
 
-    struct sockaddr_in* ifrAddr = reinterpret_cast<sockaddr_in*>(&ifr.ifr_addr);
+bool setAddr4(std::string_view ifname, std::string_view addr, std::optional<uint8_t> prefixlen) {
+    auto ifr = ifreqs::fromName(ifname);
+    auto ifrAddr = reinterpret_cast<sockaddr_in*>(&ifr.ifr_addr);
     ifrAddr->sin_family = AF_INET;
     ifrAddr->sin_addr.s_addr = inetAddr(addr);
+    if (!ifreqs::send(SIOCSIFADDR, ifr)) return false;
 
-    return ifreqs::send(SIOCSIFADDR, ifr);
+    if (prefixlen.has_value()) {
+        if (*prefixlen < 0 || *prefixlen > 32) {
+            LOG(ERROR) << "Invalid prefix length: " << *prefixlen;
+            return false;
+        }
+        ifr = ifreqs::fromName(ifname);
+        auto ifrNetmask = reinterpret_cast<sockaddr_in*>(&ifr.ifr_netmask);
+        ifrNetmask->sin_family = AF_INET;
+        ifrNetmask->sin_addr.s_addr = prefixLengthToIpv4Netmask(*prefixlen);
+        if (!ifreqs::send(SIOCSIFNETMASK, ifr)) return false;
+    }
+
+    return true;
 }
 
 bool addAddr4(std::string_view ifname, std::string_view addr, uint8_t prefixlen) {
diff --git a/automotive/can/1.0/default/libnl++/Android.bp b/automotive/can/1.0/default/libnl++/Android.bp
index 5e3168a..ade4ae0 100644
--- a/automotive/can/1.0/default/libnl++/Android.bp
+++ b/automotive/can/1.0/default/libnl++/Android.bp
@@ -25,9 +25,18 @@
 
 cc_library_static {
     name: "libnl++",
-    defaults: ["android.hardware.automotive.can@defaults"],
     host_supported: true,
     vendor_available: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+    ],
+    shared_libs: [
+        "libbase",
+        "libutils",
+    ],
     srcs: [
         "protocols/common/Empty.cpp",
         "protocols/common/Error.cpp",
diff --git a/automotive/evs/aidl/Android.bp b/automotive/evs/aidl/Android.bp
index 75eb924..8983ae4 100644
--- a/automotive/evs/aidl/Android.bp
+++ b/automotive/evs/aidl/Android.bp
@@ -55,14 +55,14 @@
             version: "1",
             imports: [
                 "android.hardware.common-V2",
-                "android.hardware.graphics.common-V5",
+                "android.hardware.graphics.common-V6",
             ],
         },
         {
             version: "2",
             imports: [
                 "android.hardware.common-V2",
-                "android.hardware.graphics.common-V5",
+                "android.hardware.graphics.common-V6",
             ],
         },
 
diff --git a/automotive/vehicle/TEST_MAPPING b/automotive/vehicle/TEST_MAPPING
index d848774..56bc047 100644
--- a/automotive/vehicle/TEST_MAPPING
+++ b/automotive/vehicle/TEST_MAPPING
@@ -48,12 +48,7 @@
       "name": "GRPCVehicleHardwareUnitTest"
     },
     {
-      "name": "CarServiceUnitTest",
-      "options" : [
-        {
-          "include-filter": "com.android.car.hal.fakevhal.FakeVehicleStubUnitTest"
-        }
-      ]
+      "name": "CarServiceHalUnitTest"
     },
     {
       "name": "VehicleHalProtoMessageConverterTest"
diff --git a/automotive/vehicle/aidl/Android.bp b/automotive/vehicle/aidl/Android.bp
index ce9e7a1..4b2d2b8 100644
--- a/automotive/vehicle/aidl/Android.bp
+++ b/automotive/vehicle/aidl/Android.bp
@@ -27,7 +27,7 @@
     srcs: [
         "android/hardware/automotive/vehicle/*.aidl",
     ],
-    frozen: true,
+    frozen: false,
     stability: "vintf",
     backend: {
         cpp: {
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/HasSupportedValueInfo.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/HasSupportedValueInfo.aidl
new file mode 100644
index 0000000..e9633cc
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/HasSupportedValueInfo.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.vehicle;
+@JavaDerive(equals=true, toString=true) @RustDerive(Clone=true) @VintfStability
+parcelable HasSupportedValueInfo {
+  boolean hasMinSupportedValue;
+  boolean hasMaxSupportedValue;
+  boolean hasSupportedValuesList;
+}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/IVehicle.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/IVehicle.aidl
index b5f62aa..ee58416 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/IVehicle.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/IVehicle.aidl
@@ -41,6 +41,10 @@
   void subscribe(in android.hardware.automotive.vehicle.IVehicleCallback callback, in android.hardware.automotive.vehicle.SubscribeOptions[] options, int maxSharedMemoryFileCount);
   void unsubscribe(in android.hardware.automotive.vehicle.IVehicleCallback callback, in int[] propIds);
   void returnSharedMemory(in android.hardware.automotive.vehicle.IVehicleCallback callback, long sharedMemoryId);
+  android.hardware.automotive.vehicle.SupportedValuesListResults getSupportedValuesLists(in List<android.hardware.automotive.vehicle.PropIdAreaId> propIdAreaIds);
+  android.hardware.automotive.vehicle.MinMaxSupportedValueResults getMinMaxSupportedValue(in List<android.hardware.automotive.vehicle.PropIdAreaId> propIdAreaIds);
+  void registerSupportedValueChangeCallback(in android.hardware.automotive.vehicle.IVehicleCallback callback, in List<android.hardware.automotive.vehicle.PropIdAreaId> propIdAreaIds);
+  void unregisterSupportedValueChangeCallback(in android.hardware.automotive.vehicle.IVehicleCallback callback, in List<android.hardware.automotive.vehicle.PropIdAreaId> propIdAreaIds);
   const long INVALID_MEMORY_ID = 0;
   const int MAX_SHARED_MEMORY_FILES_PER_CLIENT = 3;
 }
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/IVehicleCallback.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/IVehicleCallback.aidl
index 2c5a333..50a8e76 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/IVehicleCallback.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/IVehicleCallback.aidl
@@ -38,4 +38,5 @@
   oneway void onSetValues(in android.hardware.automotive.vehicle.SetValueResults responses);
   oneway void onPropertyEvent(in android.hardware.automotive.vehicle.VehiclePropValues propValues, int sharedMemoryFileCount);
   oneway void onPropertySetError(in android.hardware.automotive.vehicle.VehiclePropErrors errors);
+  oneway void onSupportedValueChange(in List<android.hardware.automotive.vehicle.PropIdAreaId> propIdAreaIds);
 }
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.aidl
new file mode 100644
index 0000000..a004b79
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.vehicle;
+@JavaDerive(equals=true, toString=true) @RustDerive(Clone=true) @VintfStability
+parcelable MinMaxSupportedValueResult {
+  android.hardware.automotive.vehicle.StatusCode status = android.hardware.automotive.vehicle.StatusCode.OK;
+  @nullable android.hardware.automotive.vehicle.RawPropValues minSupportedValue;
+  @nullable android.hardware.automotive.vehicle.RawPropValues maxSupportedValue;
+}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/MinMaxSupportedValueResults.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/MinMaxSupportedValueResults.aidl
new file mode 100644
index 0000000..914d9c6
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/MinMaxSupportedValueResults.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.vehicle;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable MinMaxSupportedValueResults {
+  android.hardware.automotive.vehicle.MinMaxSupportedValueResult[] payloads;
+  @nullable ParcelFileDescriptor sharedMemoryFd;
+}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/PropIdAreaId.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/PropIdAreaId.aidl
new file mode 100644
index 0000000..83e9c15
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/PropIdAreaId.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.vehicle;
+@JavaDerive(equals=true, toString=true) @RustDerive(Clone=true) @VintfStability
+parcelable PropIdAreaId {
+  int propId;
+  int areaId;
+}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SupportedValuesListResult.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SupportedValuesListResult.aidl
new file mode 100644
index 0000000..f348dcb
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SupportedValuesListResult.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.vehicle;
+@JavaDerive(equals=true, toString=true) @RustDerive(Clone=true) @VintfStability
+parcelable SupportedValuesListResult {
+  android.hardware.automotive.vehicle.StatusCode status = android.hardware.automotive.vehicle.StatusCode.OK;
+  @nullable List<android.hardware.automotive.vehicle.RawPropValues> supportedValuesList;
+}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SupportedValuesListResults.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SupportedValuesListResults.aidl
new file mode 100644
index 0000000..08af47c
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SupportedValuesListResults.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.vehicle;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable SupportedValuesListResults {
+  android.hardware.automotive.vehicle.SupportedValuesListResult[] payloads;
+  @nullable ParcelFileDescriptor sharedMemoryFd;
+}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/ValueRange.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/ValueRange.aidl
new file mode 100644
index 0000000..077652b
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/ValueRange.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.vehicle;
+@JavaDerive(equals=true, toString=true) @RustDerive(Clone=true) @VintfStability
+parcelable ValueRange {
+  @nullable android.hardware.automotive.vehicle.RawPropValues minValue;
+  @nullable android.hardware.automotive.vehicle.RawPropValues maxValue;
+  @nullable List<android.hardware.automotive.vehicle.RawPropValues> supportedValues;
+}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
index eb3028e..465d9d4 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
@@ -44,4 +44,5 @@
   @nullable long[] supportedEnumValues;
   android.hardware.automotive.vehicle.VehiclePropertyAccess access = android.hardware.automotive.vehicle.VehiclePropertyAccess.NONE;
   boolean supportVariableUpdateRate;
+  @nullable android.hardware.automotive.vehicle.HasSupportedValueInfo hasSupportedValueInfo;
 }
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/HasSupportedValueInfo.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/HasSupportedValueInfo.aidl
new file mode 100644
index 0000000..991631b
--- /dev/null
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/HasSupportedValueInfo.aidl
@@ -0,0 +1,84 @@
+/*
+ * 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.automotive.vehicle;
+
+/**
+ * Whether the [propId, areaId] has min/max supported value or supported values
+ * list specified.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+@RustDerive(Clone=true)
+parcelable HasSupportedValueInfo {
+    /**
+     * Whether [propId, areaId] has min supported value specified.
+     *
+     * If this is {@code true}, the hardware specifies a min supported value.
+     * If {@code MinMaxSupportedValueResult}'s {@code status} is
+     * {@code StatusCode.OK}, its {@code minSupportedValue} must not be
+     * {@code null}.
+     *
+     * If this is {@code false}, {@code minSupportedValue} must be {@code null}.
+     *
+     * Unless otherwise specified, this field is set to {@code false} for any
+     * properties whose type is not int32, int64 or float.
+     *
+     * For certain properties, e.g. {@code EV_BRAKE_REGENERATION_LEVEL}, this
+     * must always be {@code true}. Check {@code VehicleProperty}
+     * documentation.
+     */
+    boolean hasMinSupportedValue;
+
+    /**
+     * Whether [propId, areaId] has max supported value specified.
+     *
+     * If this is {@code true}, the hardware specifies a max supported value.
+     * If {@code MinMaxSupportedValueResult}'s {@code status} is
+     * {@code StatusCode.OK}, its {@code maxSupportedValue} must not be
+     * {@code null}.
+     *
+     * If this is {@code false}, {@code maxSupportedValue} must be {@code null}.
+     *
+     * Unless otherwise specified, this field is set to {@code false} for any
+     * properties whose type is not int32, int64 or float.
+     *
+     * For certain properties, e.g. {@code EV_BRAKE_REGENERATION_LEVEL}, this
+     * must always be {@code true}. Check {@code VehicleProperty}
+     * documentation.
+     */
+    boolean hasMaxSupportedValue;
+
+    /**
+     * Whether [propId, areaId] has supported values list specified.
+     *
+     * If this is {@code true}, it means the hardware specifies supported
+     * values for this property.
+     * If {@code SupportedValueListResult}'s {@code status} is
+     * {@code StatusCode.OK}, its {@code supportedValuesList} must not be
+     * {@code null}.
+     *
+     * If this is {@code false}, {@code supportedValuesList} must always be
+     * {@code null}.
+     *
+     * The supported value is the superset for both the input value for writable
+     * property and the output value for readable property.
+     *
+     * For certain properties, e.g. {@code GEAR_SELECTION}, this must always be
+     * {@code true}. Check {@code VehicleProperty} documentation.
+     */
+    boolean hasSupportedValuesList;
+}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl
index c896d14..220b1da 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl
@@ -18,8 +18,11 @@
 
 import android.hardware.automotive.vehicle.GetValueRequests;
 import android.hardware.automotive.vehicle.IVehicleCallback;
+import android.hardware.automotive.vehicle.MinMaxSupportedValueResults;
+import android.hardware.automotive.vehicle.PropIdAreaId;
 import android.hardware.automotive.vehicle.SetValueRequests;
 import android.hardware.automotive.vehicle.SubscribeOptions;
+import android.hardware.automotive.vehicle.SupportedValuesListResults;
 import android.hardware.automotive.vehicle.VehiclePropConfigs;
 
 // Vehicle HAL interface.
@@ -260,4 +263,106 @@
      *    the used shared memory file to return.
      */
     void returnSharedMemory(in IVehicleCallback callback, long sharedMemoryId);
+
+    /**
+     * Gets the supported values lists for [propId, areaId]s.
+     *
+     * 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
+     * {@code SupportedValuesListResult}. If the hardware does not specify
+     * a list of supported values for it (indicated by
+     * {@code HasSupportedValueInfo.hasSupportedValuesList} being
+     * {@code false}), then {@code supportedValuesList} field will be
+     * {@code null}.
+     *
+     * The supported value list applies for both read/write operations. if
+     * it differs for read/write, this is the super-set.
+     *
+     * Must return a list of results, one for each requested [propId, areaId].
+     *
+     * The returned supported values list represents the currently supported
+     * values and may change dynamically. Caller should use
+     * {@code registerSupportedValueChangeCallback} to register for supported
+     * value range change.
+     *
+     * If unable to determine the supported values for some [propId, areaId] due
+     * to error cases, for example, unable to determine EV_CHARGE_PERCENT_LIMIT
+     * supported values due to battery in an error state,
+     * {@code SupportedValuesListResult.status} for that should be set to
+     * a non-okay {@code StatusCode}.
+     *
+     * If one of the [propId, areaId] is not supported,
+     * {@code SupportedValuesListResult.status} for that should be set to
+     * {@code StatusCode.INVALID_ARG}.
+     *
+     * This function should only return non-okay {@code StatusCode} if the whole
+     * operation failed and unable to get any results.
+     */
+    SupportedValuesListResults getSupportedValuesLists(in List<PropIdAreaId> propIdAreaIds);
+
+    /**
+     * Gets the min/max supported values for [propId, areaId]s.
+     *
+     * 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
+     * {@code MinMaxSupportedValueResult}. If the hardware does not specify
+     * min/max supported value for it (indicated by
+     * {@code HasSupportedValueInfo.hasMinSupportedValue} or
+     * {@code HasSupportedValueInfo.hasMaxSupportedValue} being
+     * {@code false}), then {@code minSupportedValue} or
+     * {@code maxSupportedValue} is {@code null}.
+     *
+     * The min/max supported values apply for both read/write operations. if
+     * they differs for read/write, they are from the super-set.
+     *
+     * Must return a list of results, one for each requested [propId, areaId].
+     *
+     * The returned min/max supported values represent the currently supported
+     * values and may change dynamically. Caller should use
+     * {@code registerSupportedValueChangeCallback} to register for supported
+     * value range change.
+     *
+     * If unable to determine the supported values for some [propId, areaId] due
+     * to error cases, for example, unable to determine HVAC_FAN_SPEED
+     * max supported value due to HVAC in an error state,
+     * {@code MinMaxSupportedValueResult.status} for that should be set to
+     * a non-okay {@code StatusCode}.
+     *
+     * If one of the [propId, areaId] is not supported,
+     * {@code MinMaxSupportedValueResult.status} for that should be set to
+     * {@code StatusCode.INVALID_ARG}.
+     *
+     * This function should only return non-okay {@code StatusCode} if the whole
+     * operation failed and unable to get any results.
+     */
+    MinMaxSupportedValueResults getMinMaxSupportedValue(in List<PropIdAreaId> propIdAreaIds);
+
+    /**
+     * Registers the supported value change callback.
+     *
+     * For the specified [propId, areaId]s,
+     * {@code callback.onSupportedValueChange} must be invoked if change
+     * happens.
+     *
+     * This always registers a new callback for the specified [propId, areaId]s
+     * if the same callback was not previously registered for them.
+     *
+     * The list of [propId, areaId]s to register must not be empty.
+     *
+     * @param callback The callback for supported value change.
+     * @param propIdAreaIds A list of [propId, areaId]s to register.
+     */
+    void registerSupportedValueChangeCallback(
+            in IVehicleCallback callback, in List<PropIdAreaId> propIdAreaIds);
+
+    /**
+     * Unregisters the supported value change callback.
+     *
+     * @param callback The callback to unregister.
+     * @param propIdAreaIds A list of [propId, areaId]s to unregister.
+     */
+    void unregisterSupportedValueChangeCallback(
+            in IVehicleCallback callback, in List<PropIdAreaId> propIdAreaIds);
 }
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicleCallback.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicleCallback.aidl
index 2b50321..7230d09 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicleCallback.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicleCallback.aidl
@@ -17,6 +17,7 @@
 package android.hardware.automotive.vehicle;
 
 import android.hardware.automotive.vehicle.GetValueResults;
+import android.hardware.automotive.vehicle.PropIdAreaId;
 import android.hardware.automotive.vehicle.SetValueResults;
 import android.hardware.automotive.vehicle.StatusCode;
 import android.hardware.automotive.vehicle.VehiclePropErrors;
@@ -96,4 +97,16 @@
      *     does not batch the errors, this may only contain one error.
      */
     oneway void onPropertySetError(in VehiclePropErrors errors);
+
+    /**
+     * Called when the min/max supported value or supported value list for
+     * the registered [propId, areaId]s changes.
+     *
+     * The caller is supposed to call {@code getMinMaxSupportedValue} or
+     * {@code getSupportedValuesLists} to get the new supported value range.
+     *
+     * @param propIdAreaIds The list of [propId, areaId]s whose supported
+     *    value range changes.
+     */
+    oneway void onSupportedValueChange(in List<PropIdAreaId> propIdAreaIds);
 }
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.aidl
new file mode 100644
index 0000000..f85ad3a
--- /dev/null
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.aidl
@@ -0,0 +1,48 @@
+/*
+ * 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.automotive.vehicle;
+
+import android.hardware.automotive.vehicle.RawPropValues;
+import android.hardware.automotive.vehicle.StatusCode;
+
+/**
+ * One result returned from {@code getMinMaxSupportedValue} for one request.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+@RustDerive(Clone=true)
+parcelable MinMaxSupportedValueResult {
+    /**
+     * The status for result. If this is not OK, the operation failed for this
+     * [propId, areaId].
+     */
+    StatusCode status = StatusCode.OK;
+    /**
+     * The min supported value.
+     *
+     * If the [propId, areaId] does not specify a min supported value, this
+     * is {@code null}.
+     */
+    @nullable RawPropValues minSupportedValue;
+    /**
+     * The max supported value.
+     *
+     * If the [propId, areaId] does not specify a max supported value, this
+     * is {@code null}.
+     */
+    @nullable RawPropValues maxSupportedValue;
+}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResults.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResults.aidl
new file mode 100644
index 0000000..3579979
--- /dev/null
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResults.aidl
@@ -0,0 +1,55 @@
+/*
+ * 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.automotive.vehicle;
+
+import android.hardware.automotive.vehicle.MinMaxSupportedValueResult;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * The result structure for {@code getMinMaxSupportedValue}.
+ *
+ * Contains a list of results, one for each [propId, areaId] request. The
+ * list must contain the same number of result as the {@code propIdAreaIds}.
+ * The result must be in the same order, e.g. the first result is for the first
+ * [propId, areaId].
+ *
+ * Java Client should use
+ * {@link LargeParcelable.reconstructStableAIDLParcelable} to convert this back
+ * to a regular parcelable and then use the converted parcelable's
+ * {@code payloads} field.
+ *
+ * Native client should use
+ * {@link LargeParcelable::stableLargeParcelableToParcelable}.
+ *
+ * VHAL implementation must store the results into {@link payloads} field and
+ * use {@link LargeParcelable::parcelableToStableLargeParcelable} before
+ * sending the converted large parcelable through binder.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+parcelable MinMaxSupportedValueResults {
+    /**
+     * The list of responses if they fit the binder memory limitation.
+     */
+    MinMaxSupportedValueResult[] payloads;
+    /**
+     * Shared memory file to store responses if they exceed binder memory
+     * limitation. Created by VHAL, readable only for the client.
+     * The client must close it after reading.
+     */
+    @nullable ParcelFileDescriptor sharedMemoryFd;
+}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/PropIdAreaId.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/PropIdAreaId.aidl
new file mode 100644
index 0000000..ea47350
--- /dev/null
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/PropIdAreaId.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.automotive.vehicle;
+
+import android.hardware.automotive.vehicle.RawPropValues;
+
+/**
+ * A structure containing one propertyId and one areaId.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+@RustDerive(Clone=true)
+parcelable PropIdAreaId {
+    /** The property Id. */
+    int propId;
+    /** The area Id. */
+    int areaId;
+}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SupportedValuesListResult.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SupportedValuesListResult.aidl
new file mode 100644
index 0000000..4524f4f
--- /dev/null
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SupportedValuesListResult.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.automotive.vehicle;
+
+import android.hardware.automotive.vehicle.RawPropValues;
+import android.hardware.automotive.vehicle.StatusCode;
+
+/**
+ * One result returned from {@code getSupportedValuesLists} for one request.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+@RustDerive(Clone=true)
+parcelable SupportedValuesListResult {
+    /**
+     * The status for result. If this is not OK, the operation failed for this
+     * [propId, areaId].
+     */
+    StatusCode status = StatusCode.OK;
+    /**
+     * The supported values list.
+     *
+     * If the [propId, areaId] does not specify a supported values list, this
+     * is {@code null}.
+     */
+    @nullable List<RawPropValues> supportedValuesList;
+}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SupportedValuesListResults.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SupportedValuesListResults.aidl
new file mode 100644
index 0000000..da84871
--- /dev/null
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SupportedValuesListResults.aidl
@@ -0,0 +1,55 @@
+/*
+ * 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.automotive.vehicle;
+
+import android.hardware.automotive.vehicle.SupportedValuesListResult;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * The result structure for {@code getSupportedValuesLists}.
+ *
+ * Contains a list of results, one for each [propId, areaId] request. The
+ * list must contain the same number of result as the {@code propIdAreaIds}.
+ * The result must be in the same order, e.g. the first result is for the first
+ * [propId, areaId].
+ *
+ * Java Client should use
+ * {@link LargeParcelable.reconstructStableAIDLParcelable} to convert this back
+ * to a regular parcelable and then use the converted parcelable's
+ * {@code payloads} field.
+ *
+ * Native client should use
+ * {@link LargeParcelable::stableLargeParcelableToParcelable}.
+ *
+ * VHAL implementation must store the results into {@link payloads} field and
+ * use {@link LargeParcelable::parcelableToStableLargeParcelable} before
+ * sending the converted large parcelable through binder.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+parcelable SupportedValuesListResults {
+    /**
+     * The list of responses if they fit the binder memory limitation.
+     */
+    SupportedValuesListResult[] payloads;
+    /**
+     * Shared memory file to store responses if they exceed binder memory
+     * limitation. Created by VHAL, readable only for the client.
+     * The client must close it after reading.
+     */
+    @nullable ParcelFileDescriptor sharedMemoryFd;
+}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/ValueRange.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/ValueRange.aidl
new file mode 100644
index 0000000..816a6a8
--- /dev/null
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/ValueRange.aidl
@@ -0,0 +1,55 @@
+/*
+ * 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.automotive.vehicle;
+
+import android.hardware.automotive.vehicle.RawPropValues;
+
+/**
+ * Represents the value range for a vehicle property.
+ *
+ * This applies for the values read from VHAL and the values sent to VHAL.
+ *
+ * If the value range differs, this is the super set.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+@RustDerive(Clone=true)
+parcelable ValueRange {
+    /**
+     * The currently specified minimum value for the property.
+     *
+     * {@code null} if not specified.
+     */
+    @nullable RawPropValues minValue;
+
+    /**
+     * The currently specified maximum value for the property.
+     *
+     * {@code null} if not specified.
+     */
+    @nullable RawPropValues maxValue;
+
+    /**
+     * The currently specified supported values for the property.
+     *
+     * If the value type is int32, int64 or float, the returned list must be
+     * sorted in ascending order.
+     *
+     * {@code null} if not specified.
+     */
+    @nullable List<RawPropValues> supportedValues;
+}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
index 9387965..70a5566 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
@@ -16,6 +16,7 @@
 
 package android.hardware.automotive.vehicle;
 
+import android.hardware.automotive.vehicle.HasSupportedValueInfo;
 import android.hardware.automotive.vehicle.VehiclePropertyAccess;
 
 @VintfStability
@@ -83,6 +84,8 @@
     /**
      * Whether variable update rate is supported.
      *
+     * This is always {@code false} for VHAL implementation < V3.
+     *
      * This applies for continuous property only.
      *
      * It is HIGHLY RECOMMENDED to support variable update rate for all non-heartbeat continuous
@@ -109,4 +112,12 @@
      * so this should be false if the property is large (e.g. a byte array of 1k in size).
      */
     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.
+     *
+     * For VHAL implementation < V4, this is always {@code null} and is not accessed.
+     */
+    @nullable HasSupportedValueInfo hasSupportedValueInfo;
 }
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
index 184c16e..d92a409 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
@@ -109,7 +109,7 @@
 
     static const int kMaxProcessedStream = 2;
     static const int kMaxStallStream = 1;
-    static const uint32_t kMaxBytesPerPixel = 2;
+    static const uint32_t kMaxBytesPerPixel = 3;
 
     class OutputThread : public android::Thread {
     public:
diff --git a/camera/device/aidl/Android.bp b/camera/device/aidl/Android.bp
index 48ae34e..291ed0d 100644
--- a/camera/device/aidl/Android.bp
+++ b/camera/device/aidl/Android.bp
@@ -44,7 +44,7 @@
                 "android.hardware.common.fmq-V1",
                 "android.hardware.camera.common-V1",
                 "android.hardware.camera.metadata-V1",
-                "android.hardware.graphics.common-V5",
+                "android.hardware.graphics.common-V6",
             ],
         },
         {
@@ -54,7 +54,7 @@
                 "android.hardware.common.fmq-V1",
                 "android.hardware.camera.common-V1",
                 "android.hardware.camera.metadata-V2",
-                "android.hardware.graphics.common-V5",
+                "android.hardware.graphics.common-V6",
             ],
         },
         {
@@ -64,7 +64,7 @@
                 "android.hardware.common.fmq-V1",
                 "android.hardware.camera.common-V1",
                 "android.hardware.camera.metadata-V3",
-                "android.hardware.graphics.common-V5",
+                "android.hardware.graphics.common-V6",
             ],
         },
 
diff --git a/camera/device/default/ExternalCameraDeviceSession.h b/camera/device/default/ExternalCameraDeviceSession.h
index 1c6ed06..ed84931 100644
--- a/camera/device/default/ExternalCameraDeviceSession.h
+++ b/camera/device/default/ExternalCameraDeviceSession.h
@@ -122,7 +122,7 @@
 
     static const int kMaxProcessedStream = 2;
     static const int kMaxStallStream = 1;
-    static const uint32_t kMaxBytesPerPixel = 2;
+    static const uint32_t kMaxBytesPerPixel = 3;
 
     class BufferRequestThread : public SimpleThread {
       public:
diff --git a/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSection.aidl b/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSection.aidl
index 138101b..5a35915 100644
--- a/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSection.aidl
+++ b/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSection.aidl
@@ -72,5 +72,6 @@
   ANDROID_AUTOMOTIVE_LENS,
   ANDROID_EXTENSION,
   ANDROID_JPEGR,
+  ANDROID_SHARED_SESSION,
   VENDOR_SECTION = 0x8000,
 }
diff --git a/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl b/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl
index 85eee08..4cdca62 100644
--- a/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl
+++ b/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl
@@ -72,5 +72,6 @@
   ANDROID_AUTOMOTIVE_LENS_START = (android.hardware.camera.metadata.CameraMetadataSection.ANDROID_AUTOMOTIVE_LENS << 16) /* 2031616 */,
   ANDROID_EXTENSION_START = (android.hardware.camera.metadata.CameraMetadataSection.ANDROID_EXTENSION << 16) /* 2097152 */,
   ANDROID_JPEGR_START = (android.hardware.camera.metadata.CameraMetadataSection.ANDROID_JPEGR << 16) /* 2162688 */,
+  ANDROID_SHARED_SESSION_START = (android.hardware.camera.metadata.CameraMetadataSection.ANDROID_SHARED_SESSION << 16) /* 2228224 */,
   VENDOR_SECTION_START = (android.hardware.camera.metadata.CameraMetadataSection.VENDOR_SECTION << 16) /* -2147483648 */,
 }
diff --git a/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSection.aidl b/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSection.aidl
index 73bcc12..2268dfd 100644
--- a/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSection.aidl
+++ b/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSection.aidl
@@ -63,5 +63,6 @@
     ANDROID_AUTOMOTIVE_LENS,
     ANDROID_EXTENSION,
     ANDROID_JPEGR,
+    ANDROID_SHARED_SESSION,
     VENDOR_SECTION = 0x8000,
 }
diff --git a/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl b/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl
index 75e7915..3ec54d4 100644
--- a/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl
+++ b/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl
@@ -65,5 +65,6 @@
     ANDROID_AUTOMOTIVE_LENS_START = CameraMetadataSection.ANDROID_AUTOMOTIVE_LENS << 16,
     ANDROID_EXTENSION_START = CameraMetadataSection.ANDROID_EXTENSION << 16,
     ANDROID_JPEGR_START = CameraMetadataSection.ANDROID_JPEGR << 16,
+    ANDROID_SHARED_SESSION_START = CameraMetadataSection.ANDROID_SHARED_SESSION << 16,
     VENDOR_SECTION_START = CameraMetadataSection.VENDOR_SECTION << 16,
 }
diff --git a/contexthub/aidl/default/Android.bp b/contexthub/aidl/default/Android.bp
index da173f9..c0b147c 100644
--- a/contexthub/aidl/default/Android.bp
+++ b/contexthub/aidl/default/Android.bp
@@ -30,6 +30,7 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
+        "liblog",
         "android.hardware.contexthub-V4-ndk",
     ],
     export_include_dirs: ["include"],
@@ -51,6 +52,7 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
+        "liblog",
         "android.hardware.contexthub-V4-ndk",
     ],
     static_libs: [
diff --git a/contexthub/aidl/default/ContextHub.cpp b/contexthub/aidl/default/ContextHub.cpp
index a915191..4ae9c09 100644
--- a/contexthub/aidl/default/ContextHub.cpp
+++ b/contexthub/aidl/default/ContextHub.cpp
@@ -16,10 +16,54 @@
 
 #include "contexthub-impl/ContextHub.h"
 
-namespace aidl::android::hardware::contexthub {
+#ifndef LOG_TAG
+#define LOG_TAG "CHRE"
+#endif
+
+#include <inttypes.h>
+#include <log/log.h>
 
 using ::ndk::ScopedAStatus;
 
+namespace aidl::android::hardware::contexthub {
+
+namespace {
+
+constexpr uint64_t kMockVendorHubId = 0x1234567812345678;
+constexpr uint64_t kMockVendorHub2Id = 0x0EADBEEFDEADBEEF;
+
+// Mock endpoints for the default implementation.
+// These endpoints just echo back any messages sent to them.
+constexpr size_t kMockEndpointCount = 4;
+const EndpointInfo kMockEndpointInfos[kMockEndpointCount] = {
+        {
+                .id = {.hubId = kMockVendorHubId, .id = UINT64_C(0x1)},
+                .type = EndpointInfo::EndpointType::GENERIC,
+                .name = "Mock Endpoint 1",
+                .version = 1,
+        },
+        {
+                .id = {.hubId = kMockVendorHubId, .id = UINT64_C(0x2)},
+                .type = EndpointInfo::EndpointType::GENERIC,
+                .name = "Mock Endpoint 2",
+                .version = 2,
+        },
+        {
+                .id = {.hubId = kMockVendorHub2Id, .id = UINT64_C(0x1)},
+                .type = EndpointInfo::EndpointType::GENERIC,
+                .name = "Mock Endpoint 3",
+                .version = 1,
+        },
+        {
+                .id = {.hubId = kMockVendorHub2Id, .id = UINT64_C(0x2)},
+                .type = EndpointInfo::EndpointType::GENERIC,
+                .name = "Mock Endpoint 4",
+                .version = 2,
+        },
+};
+
+}  // anonymous namespace
+
 ScopedAStatus ContextHub::getContextHubs(std::vector<ContextHubInfo>* out_contextHubInfos) {
     ContextHubInfo hub = {};
     hub.name = "Mock Context Hub";
@@ -112,7 +156,13 @@
     }
 }
 
-ScopedAStatus ContextHub::setTestMode(bool /* enable */) {
+ScopedAStatus ContextHub::setTestMode(bool enable) {
+    if (enable) {
+        std::unique_lock<std::mutex> lock(mEndpointMutex);
+        mEndpoints.clear();
+        mEndpointSessions.clear();
+        mEndpointCallback = nullptr;
+    }
     return ScopedAStatus::ok();
 }
 
@@ -137,6 +187,10 @@
 }
 
 ScopedAStatus ContextHub::getHubs(std::vector<HubInfo>* _aidl_return) {
+    if (_aidl_return == nullptr) {
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+
     ContextHubInfo hub = {};
     hub.name = "Mock Context Hub";
     hub.vendor = "AOSP";
@@ -158,61 +212,217 @@
     vendorHub.version = 42;
 
     HubInfo hubInfo2 = {};
-    hubInfo2.hubId = UINT64_C(0x1234567812345678);
+    hubInfo2.hubId = kMockVendorHubId;
     hubInfo2.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::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();
 };
 
-ScopedAStatus ContextHub::getEndpoints(std::vector<EndpointInfo>* /* _aidl_return */) {
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus ContextHub::getEndpoints(std::vector<EndpointInfo>* _aidl_return) {
+    if (_aidl_return == nullptr) {
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+
+    Service echoService;
+    echoService.format = Service::RpcFormat::CUSTOM;
+    echoService.serviceDescriptor = "ECHO";
+    echoService.majorVersion = 1;
+    echoService.minorVersion = 0;
+
+    for (const EndpointInfo& endpoint : kMockEndpointInfos) {
+        EndpointInfo endpointWithService(endpoint);
+        endpointWithService.services.push_back(echoService);
+        _aidl_return->push_back(std::move(endpointWithService));
+    }
+
+    return ScopedAStatus::ok();
 };
 
-ScopedAStatus ContextHub::registerEndpoint(const EndpointInfo& /* in_endpoint */) {
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus ContextHub::registerEndpoint(const EndpointInfo& in_endpoint) {
+    std::unique_lock<std::mutex> lock(mEndpointMutex);
+
+    for (const EndpointInfo& endpoint : mEndpoints) {
+        if ((endpoint.id.id == in_endpoint.id.id && endpoint.id.hubId == in_endpoint.id.hubId) ||
+            endpoint.name == in_endpoint.name) {
+            return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
+    }
+    mEndpoints.push_back(in_endpoint);
+    return ScopedAStatus::ok();
 };
 
-ScopedAStatus ContextHub::unregisterEndpoint(const EndpointInfo& /* in_endpoint */) {
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus ContextHub::unregisterEndpoint(const EndpointInfo& in_endpoint) {
+    std::unique_lock<std::mutex> lock(mEndpointMutex);
+
+    for (auto it = mEndpoints.begin(); it != mEndpoints.end(); ++it) {
+        if (it->id.id == in_endpoint.id.id && it->id.hubId == in_endpoint.id.hubId) {
+            mEndpoints.erase(it);
+            return ScopedAStatus::ok();
+        }
+    }
+    return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
 };
 
 ScopedAStatus ContextHub::registerEndpointCallback(
-        const std::shared_ptr<IEndpointCallback>& /* in_callback */) {
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+        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::vector<int32_t>* /* _aidl_return */) {
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus ContextHub::requestSessionIdRange(int32_t in_size,
+                                                std::vector<int32_t>* _aidl_return) {
+    constexpr int32_t kMaxSize = 1024;
+    if (in_size > kMaxSize || _aidl_return == nullptr) {
+        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+
+    {
+        std::lock_guard<std::mutex> lock(mEndpointMutex);
+        mMaxValidSessionId = in_size;
+    }
+
+    _aidl_return->push_back(0);
+    _aidl_return->push_back(in_size);
+    return ScopedAStatus::ok();
 };
 
 ScopedAStatus ContextHub::openEndpointSession(
-        int32_t /* in_sessionId */, const EndpointId& /* in_destination */,
-        const EndpointId& /* in_initiator */,
-        const std::optional<std::string>& /* in_serviceDescriptor */) {
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+        int32_t in_sessionId, const EndpointId& in_destination, const EndpointId& in_initiator,
+        const std::optional<std::string>& in_serviceDescriptor) {
+    // 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::unique_lock<std::mutex> lock(mEndpointMutex);
+        if (in_sessionId > mMaxValidSessionId) {
+            ALOGE("openEndpointSession: session ID %" PRId32 " is invalid", in_sessionId);
+            return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
+
+        for (const EndpointSession& session : mEndpointSessions) {
+            bool sessionAlreadyExists =
+                    (session.initiator == in_destination && session.peer == in_initiator) ||
+                    (session.peer == in_destination && session.initiator == in_initiator);
+            if (sessionAlreadyExists) {
+                ALOGD("openEndpointSession: session ID %" PRId32 " already exists", in_sessionId);
+                return (session.sessionId == in_sessionId &&
+                        session.serviceDescriptor == in_serviceDescriptor)
+                               ? ScopedAStatus::ok()
+                               : ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+            } else if (session.sessionId == in_sessionId) {
+                ALOGE("openEndpointSession: session ID %" PRId32 " is invalid: endpoint mismatch",
+                      in_sessionId);
+                return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+            }
+        }
+
+        // Verify the initiator and destination are valid endpoints
+        bool initiatorIsValid = findEndpoint(in_initiator, mEndpoints.begin(), mEndpoints.end());
+        if (!initiatorIsValid) {
+            ALOGE("openEndpointSession: initiator %" PRIu64 ":%" PRIu64 " is invalid",
+                  in_initiator.id, in_initiator.hubId);
+            return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
+        bool destinationIsValid = findEndpoint(in_destination, &kMockEndpointInfos[0],
+                                               &kMockEndpointInfos[kMockEndpointCount]);
+        if (!destinationIsValid) {
+            ALOGE("openEndpointSession: destination %" PRIu64 ":%" PRIu64 " is invalid",
+                  in_destination.id, in_destination.hubId);
+            return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
+
+        mEndpointSessions.push_back({
+                .sessionId = in_sessionId,
+                .initiator = in_initiator,
+                .peer = in_destination,
+                .serviceDescriptor = in_serviceDescriptor,
+        });
+
+        if (mEndpointCallback != nullptr) {
+            callback = mEndpointCallback;
+        }
+    }
+
+    if (callback != nullptr) {
+        callback->onEndpointSessionOpenComplete(in_sessionId);
+    }
+    return ScopedAStatus::ok();
 };
 
-ScopedAStatus ContextHub::sendMessageToEndpoint(int32_t /* in_sessionId */,
-                                                const Message& /* in_msg */) {
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus ContextHub::sendMessageToEndpoint(int32_t in_sessionId, const Message& in_msg) {
+    bool foundSession = false;
+    std::shared_ptr<IEndpointCallback> callback = nullptr;
+    {
+        std::unique_lock<std::mutex> lock(mEndpointMutex);
+
+        for (const EndpointSession& session : mEndpointSessions) {
+            if (session.sessionId == in_sessionId) {
+                foundSession = true;
+                break;
+            }
+        }
+
+        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);
+        }
+
+        // Echo the message back
+        callback->onMessageReceived(in_sessionId, in_msg);
+    }
+    return ScopedAStatus::ok();
 };
 
 ScopedAStatus ContextHub::sendMessageDeliveryStatusToEndpoint(
         int32_t /* in_sessionId */, const MessageDeliveryStatus& /* in_msgStatus */) {
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    return ScopedAStatus::ok();
 };
 
-ScopedAStatus ContextHub::closeEndpointSession(int32_t /* in_sessionId */, Reason /* in_reason */) {
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus ContextHub::closeEndpointSession(int32_t in_sessionId, Reason /* in_reason */) {
+    std::unique_lock<std::mutex> lock(mEndpointMutex);
+
+    for (auto it = mEndpointSessions.begin(); it != mEndpointSessions.end(); ++it) {
+        if (it->sessionId == in_sessionId) {
+            mEndpointSessions.erase(it);
+            return ScopedAStatus::ok();
+        }
+    }
+    ALOGE("closeEndpointSession: session ID %" PRId32 " is invalid", in_sessionId);
+    return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
 };
 
 ScopedAStatus ContextHub::endpointSessionOpenComplete(int32_t /* in_sessionId */) {
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    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 5680a77..4968878 100644
--- a/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
+++ b/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
@@ -18,6 +18,7 @@
 
 #include <aidl/android/hardware/contexthub/BnContextHub.h>
 
+#include <mutex>
 #include <unordered_set>
 #include <vector>
 
@@ -72,10 +73,37 @@
     ::ndk::ScopedAStatus endpointSessionOpenComplete(int32_t in_sessionId) override;
 
   private:
+    struct EndpointSession {
+        int32_t sessionId;
+        EndpointId initiator;
+        EndpointId peer;
+        std::optional<std::string> serviceDescriptor;
+    };
+
     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;
 };
 
 }  // namespace contexthub
diff --git a/contexthub/aidl/vts/Android.bp b/contexthub/aidl/vts/Android.bp
index 62a319e..a19b6fd 100644
--- a/contexthub/aidl/vts/Android.bp
+++ b/contexthub/aidl/vts/Android.bp
@@ -33,7 +33,7 @@
         "libbinder",
     ],
     static_libs: [
-        "android.hardware.contexthub-V3-cpp",
+        "android.hardware.contexthub-V4-cpp",
         "VtsHalContexthubUtils",
     ],
     test_suites: [
diff --git a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
index fa427a5..95a96cd 100644
--- a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
+++ b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
@@ -20,8 +20,10 @@
 
 #include <android/hardware/contexthub/BnContextHub.h>
 #include <android/hardware/contexthub/BnContextHubCallback.h>
+#include <android/hardware/contexthub/BnEndpointCallback.h>
 #include <android/hardware/contexthub/IContextHub.h>
 #include <android/hardware/contexthub/IContextHubCallback.h>
+#include <android/hardware/contexthub/IEndpointCallback.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 #include <log/log.h>
@@ -34,18 +36,25 @@
 using ::android::String16;
 using ::android::binder::Status;
 using ::android::hardware::contexthub::AsyncEventType;
+using ::android::hardware::contexthub::BnEndpointCallback;
 using ::android::hardware::contexthub::ContextHubInfo;
 using ::android::hardware::contexthub::ContextHubMessage;
+using ::android::hardware::contexthub::EndpointId;
+using ::android::hardware::contexthub::EndpointInfo;
 using ::android::hardware::contexthub::ErrorCode;
 using ::android::hardware::contexthub::HostEndpointInfo;
+using ::android::hardware::contexthub::HubInfo;
 using ::android::hardware::contexthub::IContextHub;
 using ::android::hardware::contexthub::IContextHubCallbackDefault;
+using ::android::hardware::contexthub::Message;
 using ::android::hardware::contexthub::MessageDeliveryStatus;
 using ::android::hardware::contexthub::NanoappBinary;
 using ::android::hardware::contexthub::NanoappInfo;
 using ::android::hardware::contexthub::NanoappRpcService;
 using ::android::hardware::contexthub::NanSessionRequest;
 using ::android::hardware::contexthub::NanSessionStateUpdate;
+using ::android::hardware::contexthub::Reason;
+using ::android::hardware::contexthub::Service;
 using ::android::hardware::contexthub::Setting;
 using ::android::hardware::contexthub::vts_utils::kNonExistentAppId;
 using ::android::hardware::contexthub::vts_utils::waitForCallback;
@@ -61,8 +70,14 @@
         contextHub = 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);
     }
 
+    virtual void TearDown() override { contextHub->setTestMode(/* enable= */ false); }
+
     uint32_t getHubId() { return std::get<1>(GetParam()); }
 
     void testSettingChanged(Setting setting);
@@ -465,6 +480,290 @@
     }
 }
 
+class TestEndpointCallback : public BnEndpointCallback {
+  public:
+    Status onEndpointStarted(const std::vector<EndpointInfo>& /* endpointInfos */) override {
+        return Status::ok();
+    }
+
+    Status onEndpointStopped(const std::vector<EndpointId>& /* endpointIds */,
+                             Reason /* reason */) override {
+        return Status::ok();
+    }
+
+    Status onMessageReceived(int32_t /* sessionId */, const Message& message) override {
+        mMessages.push_back(message);
+        return Status::ok();
+    }
+
+    Status onMessageDeliveryStatusReceived(int32_t /* sessionId */,
+                                           const MessageDeliveryStatus& /* msgStatus */) override {
+        return Status::ok();
+    }
+
+    Status onEndpointSessionOpenRequest(
+            int32_t /* sessionId */, const EndpointId& /* destination */,
+            const EndpointId& /* initiator */,
+            const std::optional<String16>& /* serviceDescriptor */) override {
+        return Status::ok();
+    }
+
+    Status onCloseEndpointSession(int32_t /* sessionId */, Reason /* reason */) override {
+        return Status::ok();
+    }
+
+    Status onEndpointSessionOpenComplete(int32_t /* sessionId */) override {
+        mWasOnEndpointSessionOpenCompleteCalled = true;
+        return Status::ok();
+    }
+
+    std::vector<Message> getMessages() { return mMessages; }
+
+    bool wasOnEndpointSessionOpenCompleteCalled() {
+        return mWasOnEndpointSessionOpenCompleteCalled;
+    }
+    void resetWasOnEndpointSessionOpenCompleteCalled() {
+        mWasOnEndpointSessionOpenCompleteCalled = false;
+    }
+
+  private:
+    std::vector<Message> mMessages;
+    bool mWasOnEndpointSessionOpenCompleteCalled = false;
+};
+
+TEST_P(ContextHubAidl, RegisterEndpoint) {
+    EndpointInfo endpointInfo;
+    endpointInfo.id.id = 1;
+    endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
+    endpointInfo.type = EndpointInfo::EndpointType::NATIVE;
+    endpointInfo.name = String16("Test host endpoint 1");
+    endpointInfo.version = 42;
+
+    Status status = contextHub->registerEndpoint(endpointInfo);
+    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());
+    }
+}
+
+TEST_P(ContextHubAidl, RegisterEndpointSameNameFailure) {
+    EndpointInfo endpointInfo;
+    endpointInfo.id.id = 2;
+    endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
+    endpointInfo.type = EndpointInfo::EndpointType::NATIVE;
+    endpointInfo.name = String16("Test host endpoint 2");
+    endpointInfo.version = 42;
+
+    EndpointInfo endpointInfo2;
+    endpointInfo2.id.id = 3;
+    endpointInfo2.id.hubId = 0xCAFECAFECAFECAFE;
+    endpointInfo2.type = EndpointInfo::EndpointType::NATIVE;
+    endpointInfo2.name = String16("Test host endpoint 2");
+    endpointInfo2.version = 42;
+
+    Status status = contextHub->registerEndpoint(endpointInfo);
+    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());
+    }
+
+    EXPECT_FALSE(contextHub->registerEndpoint(endpointInfo2).isOk());
+}
+
+TEST_P(ContextHubAidl, RegisterEndpointSameIdFailure) {
+    EndpointInfo endpointInfo;
+    endpointInfo.id.id = 4;
+    endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
+    endpointInfo.type = EndpointInfo::EndpointType::NATIVE;
+    endpointInfo.name = String16("Test host endpoint 4");
+    endpointInfo.version = 42;
+
+    EndpointInfo endpointInfo2;
+    endpointInfo2.id.id = 4;
+    endpointInfo2.id.hubId = 0xCAFECAFECAFECAFE;
+    endpointInfo2.type = EndpointInfo::EndpointType::NATIVE;
+    endpointInfo2.name = String16("Test host endpoint - same ID test");
+    endpointInfo2.version = 42;
+
+    Status status = contextHub->registerEndpoint(endpointInfo);
+    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());
+    }
+
+    EXPECT_FALSE(contextHub->registerEndpoint(endpointInfo2).isOk());
+}
+
+TEST_P(ContextHubAidl, UnregisterEndpoint) {
+    EndpointInfo endpointInfo;
+    endpointInfo.id.id = 6;
+    endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
+    endpointInfo.type = EndpointInfo::EndpointType::NATIVE;
+    endpointInfo.name = String16("Test host endpoint 6");
+    endpointInfo.version = 42;
+
+    Status status = contextHub->registerEndpoint(endpointInfo);
+    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());
+    }
+
+    EXPECT_TRUE(contextHub->unregisterEndpoint(endpointInfo).isOk());
+}
+
+TEST_P(ContextHubAidl, UnregisterEndpointNonexistent) {
+    EndpointInfo endpointInfo;
+    endpointInfo.id.id = 100;
+    endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
+    endpointInfo.type = EndpointInfo::EndpointType::NATIVE;
+    endpointInfo.name = String16("Test host endpoint 100");
+    endpointInfo.version = 42;
+
+    Status status = contextHub->unregisterEndpoint(endpointInfo);
+    if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
+        status.transactionError() == android::UNKNOWN_TRANSACTION) {
+        GTEST_SKIP() << "Not supported -> old API; or not implemented";
+    } else {
+        EXPECT_FALSE(status.isOk());
+    }
+}
+
+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) {
+        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
+    EndpointInfo initiatorEndpoint;
+    initiatorEndpoint.id.id = 7;
+    initiatorEndpoint.id.hubId = 0xCAFECAFECAFECAFE;
+    initiatorEndpoint.type = EndpointInfo::EndpointType::NATIVE;
+    initiatorEndpoint.name = String16("Test host endpoint 7");
+    initiatorEndpoint.version = 42;
+    EXPECT_TRUE(contextHub->registerEndpoint(initiatorEndpoint).isOk());
+
+    // Find the destination, if it exists
+    std::vector<EndpointInfo> endpoints;
+    EXPECT_TRUE(contextHub->getEndpoints(&endpoints).isOk());
+    const EndpointInfo* destinationEndpoint = nullptr;
+    for (const EndpointInfo& endpoint : endpoints) {
+        for (const Service& service : endpoint.services) {
+            if (service.serviceDescriptor == String16("ECHO")) {
+                destinationEndpoint = &endpoint;
+                break;
+            }
+        }
+    }
+    if (destinationEndpoint == nullptr) {
+        return;  // no echo service endpoint -> just return
+    }
+
+    // Request the range
+    constexpr int32_t requestedRange = 100;
+    std::vector<int32_t> range;
+    ASSERT_TRUE(contextHub->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
+                         ->openEndpointSession(sessionId, destinationEndpoint->id,
+                                               initiatorEndpoint.id,
+                                               /* in_serviceDescriptor= */ String16("ECHO"))
+                         .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) {
+        GTEST_SKIP() << "Not supported -> old API; or not implemented";
+    } else {
+        EXPECT_TRUE(status.isOk());
+    }
+
+    // Register the endpoint
+    EndpointInfo initiatorEndpoint;
+    initiatorEndpoint.id.id = 8;
+    initiatorEndpoint.id.hubId = 0xCAFECAFECAFECAFE;
+    initiatorEndpoint.type = EndpointInfo::EndpointType::NATIVE;
+    initiatorEndpoint.name = String16("Test host endpoint 7");
+    initiatorEndpoint.version = 42;
+    EXPECT_TRUE(contextHub->registerEndpoint(initiatorEndpoint).isOk());
+
+    // Find the destination, if it exists
+    std::vector<EndpointInfo> endpoints;
+    EXPECT_TRUE(contextHub->getEndpoints(&endpoints).isOk());
+    const EndpointInfo* destinationEndpoint = nullptr;
+    for (const EndpointInfo& endpoint : endpoints) {
+        for (const Service& service : endpoint.services) {
+            if (service.serviceDescriptor == String16("ECHO")) {
+                destinationEndpoint = &endpoint;
+                break;
+            }
+        }
+    }
+    if (destinationEndpoint == nullptr) {
+        return;  // no echo service endpoint -> just return
+    }
+
+    // Request the range
+    constexpr int32_t requestedRange = 100;
+    std::vector<int32_t> range;
+    ASSERT_TRUE(contextHub->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[0];
+    ASSERT_TRUE(contextHub
+                        ->openEndpointSession(sessionId, destinationEndpoint->id,
+                                              initiatorEndpoint.id,
+                                              /* in_serviceDescriptor= */ String16("ECHO"))
+                        .isOk());
+    EXPECT_TRUE(cb->wasOnEndpointSessionOpenCompleteCalled());
+
+    // Send the message
+    Message message;
+    message.flags = 0;
+    message.sequenceNumber = 0;
+    message.content.push_back(42);
+    ASSERT_TRUE(contextHub->sendMessageToEndpoint(sessionId, message).isOk());
+
+    // Check for echo
+    EXPECT_FALSE(cb->getMessages().empty());
+    EXPECT_EQ(cb->getMessages().back().content.back(), 42);
+}
+
 std::string PrintGeneratedTest(const testing::TestParamInfo<ContextHubAidl::ParamType>& info) {
     return std::string("CONTEXT_HUB_ID_") + std::to_string(std::get<1>(info.param));
 }
diff --git a/graphics/Android.bp b/graphics/Android.bp
index d768ecf..2213f15 100644
--- a/graphics/Android.bp
+++ b/graphics/Android.bp
@@ -64,14 +64,14 @@
 aidl_interface_defaults {
     name: "android.hardware.graphics.common-latest",
     imports: [
-        "android.hardware.graphics.common-V5",
+        "android.hardware.graphics.common-V6",
     ],
 }
 
 rust_defaults {
     name: "android.hardware.graphics.common-latest-rust",
     rustlibs: [
-        "android.hardware.graphics.common-V5-rust",
+        "android.hardware.graphics.common-V6-rust",
     ],
 }
 
@@ -80,7 +80,7 @@
     target: {
         linux: {
             static_libs: [
-                "android.hardware.graphics.common-V5-ndk",
+                "android.hardware.graphics.common-V6-ndk",
             ],
         },
     },
@@ -91,7 +91,7 @@
     target: {
         linux: {
             shared_libs: [
-                "android.hardware.graphics.common-V5-ndk",
+                "android.hardware.graphics.common-V6-ndk",
             ],
         },
     },
diff --git a/graphics/allocator/aidl/Android.bp b/graphics/allocator/aidl/Android.bp
index 30b341c..3f74b23 100644
--- a/graphics/allocator/aidl/Android.bp
+++ b/graphics/allocator/aidl/Android.bp
@@ -45,7 +45,7 @@
             version: "2",
             imports: [
                 "android.hardware.common-V2",
-                "android.hardware.graphics.common-V5",
+                "android.hardware.graphics.common-V6",
             ],
         },
 
diff --git a/graphics/composer/aidl/Android.bp b/graphics/composer/aidl/Android.bp
index f4264eb..655188d 100644
--- a/graphics/composer/aidl/Android.bp
+++ b/graphics/composer/aidl/Android.bp
@@ -59,21 +59,21 @@
         {
             version: "1",
             imports: [
-                "android.hardware.graphics.common-V5",
+                "android.hardware.graphics.common-V6",
                 "android.hardware.common-V2",
             ],
         },
         {
             version: "2",
             imports: [
-                "android.hardware.graphics.common-V5",
+                "android.hardware.graphics.common-V6",
                 "android.hardware.common-V2",
             ],
         },
         {
             version: "3",
             imports: [
-                "android.hardware.graphics.common-V5",
+                "android.hardware.graphics.common-V6",
                 "android.hardware.common-V2",
             ],
         },
diff --git a/graphics/mapper/4.0/utils/vts/Android.bp b/graphics/mapper/4.0/utils/vts/Android.bp
index c5f124c..1be460e 100644
--- a/graphics/mapper/4.0/utils/vts/Android.bp
+++ b/graphics/mapper/4.0/utils/vts/Android.bp
@@ -48,7 +48,7 @@
     ],
     export_static_lib_headers: [
         "android.hardware.graphics.allocator@4.0",
-        "android.hardware.graphics.common-V5-ndk",
+        "android.hardware.graphics.common-V6-ndk",
         "android.hardware.graphics.mapper@4.0",
     ],
     export_include_dirs: ["include"],
diff --git a/neuralnetworks/aidl/Android.bp b/neuralnetworks/aidl/Android.bp
index 9589750..45b34e6 100644
--- a/neuralnetworks/aidl/Android.bp
+++ b/neuralnetworks/aidl/Android.bp
@@ -43,28 +43,28 @@
             version: "1",
             imports: [
                 "android.hardware.common-V2",
-                "android.hardware.graphics.common-V5",
+                "android.hardware.graphics.common-V6",
             ],
         },
         {
             version: "2",
             imports: [
                 "android.hardware.common-V2",
-                "android.hardware.graphics.common-V5",
+                "android.hardware.graphics.common-V6",
             ],
         },
         {
             version: "3",
             imports: [
                 "android.hardware.common-V2",
-                "android.hardware.graphics.common-V5",
+                "android.hardware.graphics.common-V6",
             ],
         },
         {
             version: "4",
             imports: [
                 "android.hardware.common-V2",
-                "android.hardware.graphics.common-V5",
+                "android.hardware.graphics.common-V6",
             ],
         },
 
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/ProtocolDiscoveryConfig.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/ProtocolDiscoveryConfig.aidl
index 021dfe2..2df0d35 100644
--- a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/ProtocolDiscoveryConfig.aidl
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/ProtocolDiscoveryConfig.aidl
@@ -43,4 +43,5 @@
   byte discoveryPollKovio;
   byte discoveryPollBPrime;
   byte discoveryListenBPrime;
+  byte protocolChineseId;
 }
diff --git a/nfc/aidl/android/hardware/nfc/ProtocolDiscoveryConfig.aidl b/nfc/aidl/android/hardware/nfc/ProtocolDiscoveryConfig.aidl
index f8e3228..021e307 100644
--- a/nfc/aidl/android/hardware/nfc/ProtocolDiscoveryConfig.aidl
+++ b/nfc/aidl/android/hardware/nfc/ProtocolDiscoveryConfig.aidl
@@ -33,4 +33,5 @@
     byte discoveryPollKovio;
     byte discoveryPollBPrime;
     byte discoveryListenBPrime;
+    byte protocolChineseId;
 }
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index cfe9fa7..51afa12 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -149,7 +149,7 @@
     // The multiplier should never be higher than the AIDL version, but can be less
     // (for example, if the implementation is from an earlier version but the HAL service
     // uses the default libraries and so reports the current AIDL version).
-    EXPECT_TRUE((attestation_version / 100) <= aidl_version);
+    EXPECT_LE((attestation_version / 100), aidl_version);
 }
 
 bool avb_verification_enabled() {
@@ -160,12 +160,13 @@
 char nibble2hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
                        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
 
-// Attestations don't contain everything in key authorization lists, so we need to filter the key
-// lists to produce the lists that we expect to match the attestations.
+// Attestations don't completely align with key authorization lists, so we need to filter the lists
+// to include only the tags that are in both.
 auto kTagsToFilter = {
         Tag::CREATION_DATETIME,
         Tag::HARDWARE_TYPE,
         Tag::INCLUDE_UNIQUE_ID,
+        Tag::MODULE_HASH,
 };
 
 AuthorizationSet filtered_tags(const AuthorizationSet& set) {
@@ -234,6 +235,83 @@
     return boot_patch_level(key_characteristics_);
 }
 
+std::optional<vector<uint8_t>> KeyMintAidlTestBase::getModuleHash() {
+    if (AidlVersion() < 4) {
+        // The `MODULE_HASH` tag was introduced in v4 of the HAL; earlier versions should never
+        // expect to encounter it.
+        return std::nullopt;
+    }
+
+    // The KeyMint instance should already have been informed of the `MODULE_HASH` value for the
+    // currently running system. Generate a single attestation so we can find out what the value
+    // is.
+    auto challenge = "hello";
+    auto app_id = "foo";
+    auto params = AuthorizationSetBuilder()
+                          .EcdsaSigningKey(EcCurve::P_256)
+                          .Digest(Digest::NONE)
+                          .Authorization(TAG_NO_AUTH_REQUIRED)
+                          .AttestationChallenge(challenge)
+                          .AttestationApplicationId(app_id)
+                          .SetDefaultValidity();
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+    vector<Certificate> chain;
+    auto result = GenerateKey(params, &key_blob, &key_characteristics, &chain);
+    if (result != ErrorCode::OK) {
+        ADD_FAILURE() << "Failed to generate attestation:" << result;
+        return std::nullopt;
+    }
+    KeyBlobDeleter deleter(keymint_, key_blob);
+    if (chain.empty()) {
+        ADD_FAILURE() << "No attestation cert";
+        return std::nullopt;
+    }
+
+    // Parse the attestation record in the leaf cert.
+    X509_Ptr cert(parse_cert_blob(chain[0].encodedCertificate));
+    if (cert.get() == nullptr) {
+        ADD_FAILURE() << "Failed to parse attestation cert";
+        return std::nullopt;
+    }
+    ASN1_OCTET_STRING* attest_rec = get_attestation_record(cert.get());
+    if (attest_rec == nullptr) {
+        ADD_FAILURE() << "Failed to find attestation extension";
+        return std::nullopt;
+    }
+    AuthorizationSet att_sw_enforced;
+    AuthorizationSet att_hw_enforced;
+    uint32_t att_attestation_version;
+    uint32_t att_keymint_version;
+    SecurityLevel att_attestation_security_level;
+    SecurityLevel att_keymint_security_level;
+    vector<uint8_t> att_challenge;
+    vector<uint8_t> att_unique_id;
+    vector<uint8_t> att_app_id;
+
+    auto error = parse_attestation_record(attest_rec->data,                 //
+                                          attest_rec->length,               //
+                                          &att_attestation_version,         //
+                                          &att_attestation_security_level,  //
+                                          &att_keymint_version,             //
+                                          &att_keymint_security_level,      //
+                                          &att_challenge,                   //
+                                          &att_sw_enforced,                 //
+                                          &att_hw_enforced,                 //
+                                          &att_unique_id);
+    if (error != ErrorCode::OK) {
+        ADD_FAILURE() << "Failed to parse attestation extension";
+        return std::nullopt;
+    }
+
+    // The module hash should be present in the software-enforced list.
+    if (!att_sw_enforced.Contains(TAG_MODULE_HASH)) {
+        ADD_FAILURE() << "No TAG_MODULE_HASH in attestation extension";
+        return std::nullopt;
+    }
+    return att_sw_enforced.GetTagValue(TAG_MODULE_HASH);
+}
+
 /**
  * An API to determine device IDs attestation is required or not,
  * which is mandatory for KeyMint version 2 and first_api_level 33 or greater.
@@ -270,12 +348,7 @@
     }
 
     // Curve 25519 was included in version 2 of the KeyMint interface.
-    int32_t version = 0;
-    auto status = keymint_->getInterfaceVersion(&version);
-    if (!status.isOk()) {
-        ADD_FAILURE() << "Failed to determine interface version";
-    }
-    return version >= 2;
+    return AidlVersion() >= 2;
 }
 
 void KeyMintAidlTestBase::InitializeKeyMint(std::shared_ptr<IKeyMintDevice> keyMint) {
@@ -293,6 +366,20 @@
     os_version_ = getOsVersion();
     os_patch_level_ = getOsPatchlevel();
     vendor_patch_level_ = getVendorPatchlevel();
+
+    // TODO(b/369375199): temporary code, remove when apexd -> keystore2 -> KeyMint transmission
+    // of module info happens.
+    {
+        GTEST_LOG_(INFO) << "Setting MODULE_HASH to fake value as fallback";
+        // Ensure that a MODULE_HASH value is definitely present in KeyMint (if it's >= v4).
+        vector<uint8_t> fakeModuleHash = {
+                0xf3, 0xf1, 0x1f, 0xe5, 0x13, 0x05, 0xfe, 0xfa, 0xe9, 0xc3, 0x53,
+                0xef, 0x69, 0xdf, 0x9f, 0xd7, 0x0c, 0x1e, 0xcc, 0x2c, 0x2c, 0x62,
+                0x1f, 0x5e, 0x2c, 0x1d, 0x19, 0xa1, 0xfd, 0xac, 0xa1, 0xb4,
+        };
+        vector<KeyParameter> info = {Authorization(TAG_MODULE_HASH, fakeModuleHash)};
+        keymint_->setAdditionalAttestationInfo(info);
+    }
 }
 
 int32_t KeyMintAidlTestBase::AidlVersion() const {
@@ -320,6 +407,13 @@
 ErrorCode KeyMintAidlTestBase::GenerateKey(const AuthorizationSet& key_desc,
                                            vector<uint8_t>* key_blob,
                                            vector<KeyCharacteristics>* key_characteristics) {
+    return GenerateKey(key_desc, key_blob, key_characteristics, &cert_chain_);
+}
+
+ErrorCode KeyMintAidlTestBase::GenerateKey(const AuthorizationSet& key_desc,
+                                           vector<uint8_t>* key_blob,
+                                           vector<KeyCharacteristics>* key_characteristics,
+                                           vector<Certificate>* cert_chain) {
     std::optional<AttestationKey> attest_key = std::nullopt;
     vector<Certificate> attest_cert_chain;
     // If an attestation is requested, but the system is RKP-only, we need to supply an explicit
@@ -340,11 +434,10 @@
         attest_key.value().issuerSubjectName = make_name_from_str("Android Keystore Key");
     }
 
-    ErrorCode error =
-            GenerateKey(key_desc, attest_key, key_blob, key_characteristics, &cert_chain_);
+    ErrorCode error = GenerateKey(key_desc, attest_key, key_blob, key_characteristics, cert_chain);
 
     if (error == ErrorCode::OK && attest_cert_chain.size() > 0) {
-        cert_chain_.push_back(attest_cert_chain[0]);
+        cert_chain->push_back(attest_cert_chain[0]);
     }
 
     return error;
@@ -1049,13 +1142,12 @@
                 int openssl_padding = RSA_NO_PADDING;
                 switch (padding) {
                     case PaddingMode::NONE:
-                        ASSERT_TRUE(data_size <= key_len);
+                        ASSERT_LE(data_size, key_len);
                         ASSERT_EQ(key_len, signature.size());
                         openssl_padding = RSA_NO_PADDING;
                         break;
                     case PaddingMode::RSA_PKCS1_1_5_SIGN:
-                        ASSERT_TRUE(data_size + kPkcs1UndigestedSignaturePaddingOverhead <=
-                                    key_len);
+                        ASSERT_LE(data_size + kPkcs1UndigestedSignaturePaddingOverhead, key_len);
                         openssl_padding = RSA_PKCS1_PADDING;
                         break;
                     default:
@@ -1812,7 +1904,7 @@
         }
     }
 
-    // Verified boot key should be all 0's if the boot state is not verified or self signed
+    // Verified Boot key should be all zeroes if the boot state is "orange".
     std::string empty_boot_key(32, '\0');
     std::string verified_boot_key_str((const char*)verified_boot_key.data(),
                                       verified_boot_key.size());
@@ -2271,7 +2363,7 @@
         // ATTESTATION_IDS_NOT_PROVISIONED in this case.
         ASSERT_TRUE((tag == TAG_ATTESTATION_ID_IMEI || tag == TAG_ATTESTATION_ID_MEID ||
                      tag == TAG_ATTESTATION_ID_SECOND_IMEI))
-                << "incorrect error code on attestation ID mismatch";
+                << "incorrect error code on attestation ID mismatch for " << tag;
     } else {
         ADD_FAILURE() << "Error code " << result
                       << " returned on attestation ID mismatch, should be CANNOT_ATTEST_IDS";
@@ -2353,7 +2445,7 @@
 
     FILE* pipe = popen(command.c_str(), "r");
     if (!pipe) {
-        LOG(ERROR) << "popen failed.";
+        GTEST_LOG_(ERROR) << "popen failed.";
         return result;
     }
 
@@ -2379,7 +2471,7 @@
     std::string output = exec_command(cmd);
 
     if (output.empty()) {
-        LOG(ERROR) << "Command failed. Cmd: " << cmd;
+        GTEST_LOG_(ERROR) << "Command failed. Cmd: " << cmd;
         return "";
     }
 
@@ -2387,13 +2479,14 @@
             ::android::base::Tokenize(::android::base::Trim(output), "Device IMEI:");
 
     if (out.size() != 1) {
-        LOG(ERROR) << "Error in parsing the command output. Cmd: " << cmd;
+        GTEST_LOG_(ERROR) << "Error in parsing the command output. Cmd: " << cmd;
         return "";
     }
 
     std::string imei = ::android::base::Trim(out[0]);
     if (imei.compare("null") == 0) {
-        LOG(WARNING) << "Failed to get IMEI from Telephony service: value is null. Cmd: " << cmd;
+        GTEST_LOG_(WARNING) << "Failed to get IMEI from Telephony service: value is null. Cmd: "
+                            << cmd;
         return "";
     }
 
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index 85ae93d..1c12136 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -103,6 +103,7 @@
     uint32_t vendor_patch_level() { return vendor_patch_level_; }
     uint32_t boot_patch_level(const vector<KeyCharacteristics>& key_characteristics);
     uint32_t boot_patch_level();
+    std::optional<vector<uint8_t>> getModuleHash();
     bool isDeviceIdAttestationRequired();
     bool isSecondImeiIdAttestationRequired();
     std::optional<bool> isRkpOnly();
@@ -114,6 +115,10 @@
     ErrorCode GenerateKey(const AuthorizationSet& key_desc, vector<uint8_t>* key_blob,
                           vector<KeyCharacteristics>* key_characteristics);
 
+    ErrorCode GenerateKey(const AuthorizationSet& key_desc, vector<uint8_t>* key_blob,
+                          vector<KeyCharacteristics>* key_characteristics,
+                          vector<Certificate>* cert_chain);
+
     ErrorCode GenerateKey(const AuthorizationSet& key_desc,
                           const optional<AttestationKey>& attest_key, vector<uint8_t>* key_blob,
                           vector<KeyCharacteristics>* key_characteristics,
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index 527b5e0..067db78 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -643,6 +643,7 @@
         // Verify that App data, ROT and auth timeout are NOT included.
         EXPECT_FALSE(auths.Contains(TAG_ROOT_OF_TRUST));
         EXPECT_FALSE(auths.Contains(TAG_APPLICATION_DATA));
+        EXPECT_FALSE(auths.Contains(TAG_MODULE_HASH));
         EXPECT_FALSE(auths.Contains(TAG_AUTH_TIMEOUT, 301U));
 
         // None of the tests specify CREATION_DATETIME so check that the KeyMint implementation
@@ -2583,7 +2584,8 @@
     auto result = GenerateKey(
             AuthorizationSetBuilder().EcdsaKey(256).Digest(Digest::NONE).SetDefaultValidity());
     ASSERT_TRUE(result == ErrorCode::UNSUPPORTED_KEY_SIZE ||
-                result == ErrorCode::UNSUPPORTED_EC_CURVE);
+                result == ErrorCode::UNSUPPORTED_EC_CURVE)
+            << "unexpected result " << result;
 }
 
 /*
@@ -2604,7 +2606,7 @@
                                       .SigningKey()
                                       .Digest(Digest::NONE)
                                       .SetDefaultValidity());
-    ASSERT_TRUE(result == ErrorCode::INVALID_ARGUMENT);
+    ASSERT_EQ(result, ErrorCode::INVALID_ARGUMENT);
 }
 
 /*
@@ -3183,7 +3185,8 @@
     string result;
     ErrorCode finish_error_code = Finish(message, &result);
     EXPECT_TRUE(finish_error_code == ErrorCode::INVALID_INPUT_LENGTH ||
-                finish_error_code == ErrorCode::INVALID_ARGUMENT);
+                finish_error_code == ErrorCode::INVALID_ARGUMENT)
+            << "unexpected error code " << finish_error_code;
 
     // Very large message that should exceed the transfer buffer size of any reasonable TEE.
     message = string(128 * 1024, 'a');
@@ -3193,7 +3196,8 @@
                                               .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
     finish_error_code = Finish(message, &result);
     EXPECT_TRUE(finish_error_code == ErrorCode::INVALID_INPUT_LENGTH ||
-                finish_error_code == ErrorCode::INVALID_ARGUMENT);
+                finish_error_code == ErrorCode::INVALID_ARGUMENT)
+            << "unexpected error code " << finish_error_code;
 }
 
 /*
@@ -3247,7 +3251,8 @@
                                                   .Digest(Digest::NONE)
                                                   .Digest(Digest::SHA1)
                                                   .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN));
-    ASSERT_TRUE(result == ErrorCode::UNSUPPORTED_DIGEST || result == ErrorCode::INVALID_ARGUMENT);
+    ASSERT_TRUE(result == ErrorCode::UNSUPPORTED_DIGEST || result == ErrorCode::INVALID_ARGUMENT)
+            << "unexpected result " << result;
 
     ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
               Begin(KeyPurpose::SIGN,
@@ -3420,7 +3425,8 @@
         }
 
         auto rc = DeleteKey();
-        ASSERT_TRUE(rc == ErrorCode::OK || rc == ErrorCode::UNIMPLEMENTED);
+        ASSERT_TRUE(rc == ErrorCode::OK || rc == ErrorCode::UNIMPLEMENTED)
+                << "unexpected result " << rc;
     }
 }
 
@@ -5704,7 +5710,8 @@
     // is checked against those values, and found absent.
     auto result = Begin(KeyPurpose::DECRYPT, params);
     EXPECT_TRUE(result == ErrorCode::UNSUPPORTED_MGF_DIGEST ||
-                result == ErrorCode::INCOMPATIBLE_MGF_DIGEST);
+                result == ErrorCode::INCOMPATIBLE_MGF_DIGEST)
+            << "unexpected result " << result;
 }
 
 /*
@@ -5969,14 +5976,16 @@
                                                      .BlockMode(BlockMode::ECB)
                                                      .Padding(PaddingMode::NONE));
     EXPECT_TRUE(result == ErrorCode::INCOMPATIBLE_BLOCK_MODE ||
-                result == ErrorCode::UNSUPPORTED_BLOCK_MODE);
+                result == ErrorCode::UNSUPPORTED_BLOCK_MODE)
+            << "unexpected result " << result;
 
     result = Begin(KeyPurpose::ENCRYPT, AuthorizationSetBuilder()
                                                 .BlockMode(BlockMode::ECB)
                                                 .Padding(PaddingMode::NONE)
                                                 .Padding(PaddingMode::PKCS7));
     EXPECT_TRUE(result == ErrorCode::INCOMPATIBLE_PADDING_MODE ||
-                result == ErrorCode::UNSUPPORTED_PADDING_MODE);
+                result == ErrorCode::UNSUPPORTED_PADDING_MODE)
+            << "unexpected result " << result;
 }
 
 /*
@@ -8759,7 +8768,8 @@
 // Re-enable and run at your own risk.
 TEST_P(DestroyAttestationIdsTest, DISABLED_DestroyTest) {
     auto result = DestroyAttestationIds();
-    EXPECT_TRUE(result == ErrorCode::OK || result == ErrorCode::UNIMPLEMENTED);
+    EXPECT_TRUE(result == ErrorCode::OK || result == ErrorCode::UNIMPLEMENTED)
+            << "unexpected result " << result;
 }
 
 INSTANTIATE_KEYMINT_AIDL_TEST(DestroyAttestationIdsTest);
@@ -8892,6 +8902,49 @@
 
 INSTANTIATE_KEYMINT_AIDL_TEST(EarlyBootKeyTest);
 
+using ModuleHashTest = KeyMintAidlTestBase;
+
+TEST_P(ModuleHashTest, SetSameValue) {
+    if (AidlVersion() < 4) {
+        GTEST_SKIP() << "Module hash only available for >= v4, this device is v" << AidlVersion();
+    }
+    auto module_hash = getModuleHash();
+    ASSERT_TRUE(module_hash.has_value());
+
+    // Setting the same value that's already there should work.
+    vector<KeyParameter> info = {Authorization(TAG_MODULE_HASH, module_hash.value())};
+    EXPECT_TRUE(keymint_->setAdditionalAttestationInfo(info).isOk());
+}
+
+TEST_P(ModuleHashTest, SetDifferentValue) {
+    if (AidlVersion() < 4) {
+        GTEST_SKIP() << "Module hash only available for >= v4, this device is v" << AidlVersion();
+    }
+    auto module_hash = getModuleHash();
+    ASSERT_TRUE(module_hash.has_value());
+    vector<uint8_t> wrong_hash = module_hash.value();
+    ASSERT_EQ(wrong_hash.size(), 32);
+
+    // Setting a slightly different value should fail.
+    wrong_hash[0] ^= 0x01;
+    vector<KeyParameter> info = {Authorization(TAG_MODULE_HASH, wrong_hash)};
+    EXPECT_EQ(GetReturnErrorCode(keymint_->setAdditionalAttestationInfo(info)),
+              ErrorCode::MODULE_HASH_ALREADY_SET);
+}
+
+TEST_P(ModuleHashTest, SetUnrelatedTag) {
+    if (AidlVersion() < 4) {
+        GTEST_SKIP() << "Module hash only available for >= v4, this device is v" << AidlVersion();
+    }
+
+    // Trying to set an unexpected tag should be silently ignored..
+    vector<uint8_t> data = {0, 1, 2, 3, 4};
+    vector<KeyParameter> info = {Authorization(TAG_ROOT_OF_TRUST, data)};
+    EXPECT_EQ(GetReturnErrorCode(keymint_->setAdditionalAttestationInfo(info)), ErrorCode::OK);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(ModuleHashTest);
+
 using VsrRequirementTest = KeyMintAidlTestBase;
 
 // @VsrTest = VSR-3.10-008
@@ -8912,6 +8965,18 @@
     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;
+    }
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        GTEST_SKIP() << "Applies only to TEE KeyMint, not StrongBox KeyMint";
+    }
+    EXPECT_GE(AidlVersion(), 4) << "VSR 16+ requires KeyMint version 4 in TEE";
+}
+
 INSTANTIATE_KEYMINT_AIDL_TEST(VsrRequirementTest);
 
 class InstanceTest : public testing::Test {
diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp
index 271e36c..7050141 100644
--- a/security/keymint/support/Android.bp
+++ b/security/keymint/support/Android.bp
@@ -30,6 +30,7 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-DKEYMINT_HAL_V4",
     ],
     srcs: [
         "attestation_record.cpp",
diff --git a/security/keymint/support/attestation_record.cpp b/security/keymint/support/attestation_record.cpp
index 5a26611..d2cbf88 100644
--- a/security/keymint/support/attestation_record.cpp
+++ b/security/keymint/support/attestation_record.cpp
@@ -106,6 +106,7 @@
     ASN1_NULL* device_unique_attestation;
     ASN1_NULL* identity_credential;
     ASN1_OCTET_STRING* attestation_id_second_imei;
+    ASN1_OCTET_STRING* module_hash;
 } KM_AUTH_LIST;
 
 ASN1_SEQUENCE(KM_AUTH_LIST) = {
@@ -173,6 +174,7 @@
                      TAG_IDENTITY_CREDENTIAL_KEY.maskedTag()),
         ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_second_imei, ASN1_OCTET_STRING,
                      TAG_ATTESTATION_ID_SECOND_IMEI.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, module_hash, ASN1_OCTET_STRING, TAG_MODULE_HASH.maskedTag()),
 } ASN1_SEQUENCE_END(KM_AUTH_LIST);
 IMPLEMENT_ASN1_FUNCTIONS(KM_AUTH_LIST);
 
@@ -327,6 +329,7 @@
     copyAuthTag(record->device_unique_attestation, TAG_DEVICE_UNIQUE_ATTESTATION, auth_list);
     copyAuthTag(record->identity_credential, TAG_IDENTITY_CREDENTIAL_KEY, auth_list);
     copyAuthTag(record->attestation_id_second_imei, TAG_ATTESTATION_ID_SECOND_IMEI, auth_list);
+    copyAuthTag(record->module_hash, TAG_MODULE_HASH, auth_list);
 
     return ErrorCode::OK;
 }
diff --git a/security/keymint/support/include/keymint_support/keymint_tags.h b/security/keymint/support/include/keymint_support/keymint_tags.h
index 823899a..89c9c0b 100644
--- a/security/keymint/support/include/keymint_support/keymint_tags.h
+++ b/security/keymint/support/include/keymint_support/keymint_tags.h
@@ -103,6 +103,15 @@
 DECLARE_TYPED_TAG(MAX_USES_PER_BOOT);
 DECLARE_TYPED_TAG(MIN_MAC_LENGTH);
 DECLARE_TYPED_TAG(MIN_SECONDS_BETWEEN_OPS);
+// TODO: remove special case macro once v4 HAL is frozen
+#ifdef KEYMINT_HAL_V4
+DECLARE_TYPED_TAG(MODULE_HASH);
+#else
+// When building for previous frozen HAL, the `Tag::MODULE_NAME` constant is not available.
+static const Tag Tag_MODULE_HASH = static_cast<Tag>(-1879047468);
+typedef typename Tag2TypedTag<Tag_MODULE_HASH>::type TAG_MODULE_HASH_t;
+static TAG_MODULE_HASH_t TAG_MODULE_HASH;
+#endif
 DECLARE_TYPED_TAG(NONCE);
 DECLARE_TYPED_TAG(NO_AUTH_REQUIRED);
 DECLARE_TYPED_TAG(ORIGIN);
diff --git a/staging/security/see/hwcrypto/aidl/Android.bp b/staging/security/see/hwcrypto/aidl/Android.bp
index 0a7e8be..2da59a4 100644
--- a/staging/security/see/hwcrypto/aidl/Android.bp
+++ b/staging/security/see/hwcrypto/aidl/Android.bp
@@ -28,4 +28,5 @@
             enabled: true,
         },
     },
+    frozen: false,
 }
diff --git a/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
index 3763f0a..5b34572 100644
--- a/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
+++ b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
@@ -38,10 +38,16 @@
   android.hardware.security.see.hwcrypto.IHwCryptoKey.DerivedKey deriveKey(in android.hardware.security.see.hwcrypto.IHwCryptoKey.DerivedKeyParameters parameters);
   android.hardware.security.see.hwcrypto.IHwCryptoOperations getHwCryptoOperations();
   android.hardware.security.see.hwcrypto.IOpaqueKey importClearKey(in android.hardware.security.see.hwcrypto.types.ExplicitKeyMaterial keyMaterial, in android.hardware.security.see.hwcrypto.KeyPolicy newKeyPolicy);
+  byte[] getCurrentDicePolicy();
+  android.hardware.security.see.hwcrypto.IOpaqueKey keyTokenImport(in android.hardware.security.see.hwcrypto.types.OpaqueKeyToken requestedKey, in byte[] sealingDicePolicy);
+  android.hardware.security.see.hwcrypto.IOpaqueKey getKeyslotData(android.hardware.security.see.hwcrypto.IHwCryptoKey.KeySlot slotId);
   enum DeviceKeyId {
     DEVICE_BOUND_KEY,
     BATCH_KEY,
   }
+  enum KeySlot {
+    KEYMINT_SHARED_HMAC_KEY,
+  }
   union DiceBoundDerivationKey {
     android.hardware.security.see.hwcrypto.IOpaqueKey opaqueKey;
     android.hardware.security.see.hwcrypto.IHwCryptoKey.DeviceKeyId keyId;
diff --git a/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/IOpaqueKey.aidl b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/IOpaqueKey.aidl
index 9cbf272..88dbdf1 100644
--- a/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/IOpaqueKey.aidl
+++ b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/IOpaqueKey.aidl
@@ -36,4 +36,6 @@
   byte[] exportWrappedKey(in android.hardware.security.see.hwcrypto.IOpaqueKey wrappingKey);
   android.hardware.security.see.hwcrypto.KeyPolicy getKeyPolicy();
   byte[] getPublicKey();
+  android.hardware.security.see.hwcrypto.types.OpaqueKeyToken getShareableToken(in byte[] sealingDicePolicy);
+  void setProtectionId(in android.hardware.security.see.hwcrypto.types.ProtectionId protectionId, in android.hardware.security.see.hwcrypto.types.OperationType[] allowedOperations);
 }
diff --git a/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/OperationParameters.aidl b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/OperationParameters.aidl
index 017e51c..e069610 100644
--- a/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/OperationParameters.aidl
+++ b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/OperationParameters.aidl
@@ -35,4 +35,5 @@
 union OperationParameters {
   android.hardware.security.see.hwcrypto.types.SymmetricAuthOperationParameters symmetricAuthCrypto;
   android.hardware.security.see.hwcrypto.types.SymmetricOperationParameters symmetricCrypto;
+  android.hardware.security.see.hwcrypto.types.HmacOperationParameters hmac;
 }
diff --git a/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/ExplicitKeyMaterial.aidl b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/ExplicitKeyMaterial.aidl
index 933fb67..9970678 100644
--- a/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/ExplicitKeyMaterial.aidl
+++ b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/ExplicitKeyMaterial.aidl
@@ -34,4 +34,5 @@
 package android.hardware.security.see.hwcrypto.types;
 union ExplicitKeyMaterial {
   android.hardware.security.see.hwcrypto.types.AesKey aes;
+  android.hardware.security.see.hwcrypto.types.HmacKey hmac;
 }
diff --git a/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/HalErrorCode.aidl b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/HalErrorCode.aidl
index cd8b3c6..742314c 100644
--- a/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/HalErrorCode.aidl
+++ b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/HalErrorCode.aidl
@@ -41,4 +41,5 @@
   const int ALLOCATION_ERROR = (-5) /* -5 */;
   const int INVALID_KEY = (-6) /* -6 */;
   const int BAD_PARAMETER = (-7) /* -7 */;
+  const int UNAUTHORIZED = (-8) /* -8 */;
 }
diff --git a/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/HmacKey.aidl b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/HmacKey.aidl
new file mode 100644
index 0000000..f8de94a
--- /dev/null
+++ b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/HmacKey.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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.see.hwcrypto.types;
+union HmacKey {
+  byte[32] sha256 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  byte[64] sha512;
+}
diff --git a/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/HmacOperationParameters.aidl b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/HmacOperationParameters.aidl
new file mode 100644
index 0000000..532cd8d
--- /dev/null
+++ b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/HmacOperationParameters.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.see.hwcrypto.types;
+parcelable HmacOperationParameters {
+  android.hardware.security.see.hwcrypto.IOpaqueKey key;
+}
diff --git a/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/OpaqueKeyToken.aidl b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/OpaqueKeyToken.aidl
new file mode 100644
index 0000000..fc2dd63
--- /dev/null
+++ b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/OpaqueKeyToken.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.see.hwcrypto.types;
+parcelable OpaqueKeyToken {
+  byte[] keyToken;
+}
diff --git a/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/ProtectionId.aidl b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/ProtectionId.aidl
new file mode 100644
index 0000000..1e304ab
--- /dev/null
+++ b/staging/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see/current/android/hardware/security/see/hwcrypto/types/ProtectionId.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.see.hwcrypto.types;
+enum ProtectionId {
+  WIDEVINE_OUTPUT_BUFFER = 1,
+}
diff --git a/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
index b5e7e9d..bb194a3 100644
--- a/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
+++ b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
@@ -19,6 +19,7 @@
 import android.hardware.security.see.hwcrypto.IOpaqueKey;
 import android.hardware.security.see.hwcrypto.KeyPolicy;
 import android.hardware.security.see.hwcrypto.types.ExplicitKeyMaterial;
+import android.hardware.security.see.hwcrypto.types.OpaqueKeyToken;
 
 /*
  * Higher level interface to access and generate keys.
@@ -36,6 +37,19 @@
         DEVICE_BOUND_KEY,
         BATCH_KEY,
     }
+
+    /*
+     * Identifier for the requested key slot. The currently supported identifiers are:
+     *
+     * KEYMINT_SHARED_HMAC_KEY:
+     *      This is the shared HMAC key that will now be computed by HwCryptoKey after participating
+     *      in the ISharedSecret protocol that can be shared with KeyMint and authenticators. See
+     *      ISharedSecret.aidl for more information.
+     */
+    enum KeySlot {
+        KEYMINT_SHARED_HMAC_KEY,
+    }
+
     union DiceBoundDerivationKey {
         /*
          * Opaque to be used to derive the DICE bound key.
@@ -217,4 +231,59 @@
      *      otherwise.
      */
     IOpaqueKey importClearKey(in ExplicitKeyMaterial keyMaterial, in KeyPolicy newKeyPolicy);
+
+    /*
+     * getCurrentDicePolicy() - Returns the client current DICE policy. This policy is encrypted and
+     *                          considered opaque from the client perspective. This policy is the
+     *                          same used to create DICE bound keys and will also be used to seal
+     *                          secrets that can only be retrieved by the DICE policy owner. The
+     *                          first use of this seal operation will be
+     *                          <code>IOpaqueKey::getShareableToken</code> and
+     *                          <code>IHwCryptoKey::keyTokenImport</code>. To start this process,
+     *                          the intended key receiver will call this function and then pass the
+     *                          generated DICE policy to the owner of the key that the receiver
+     *                          wants to import. The key owner will then call
+     *                          <code>IOpaqueKey::getShareableToken</code> 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[] getCurrentDicePolicy();
+
+    /*
+     * key_token_import() - Imports a key from a different client service instance. Because
+     *                      IOpaqueKey are binder objects that cannot be directly shared between
+     *                      binder rpc clients, this method provide a way to send a key to another
+     *                      client. Keys to be imported by the receiver are represented by a token
+     *                      created using <code>IOpaqueKey::getShareableToken</code>. The flow
+     *                      to create this token is described in
+     *                      <code>IHwCryptoKey::getCurrentDicePolicy</code>.
+     *
+     * @requested_key:
+     *      Handle to the key to be imported to the caller service.
+     * @sealingDicePolicy:
+     *      DICE policy used to seal the exported key.
+     * Return:
+     *      A IOpaqueKey that can be directly be used on the local HWCrypto service on
+     *      success, service specific error based on <code>HalErrorCode</code> otherwise.
+     */
+    IOpaqueKey keyTokenImport(in OpaqueKeyToken requestedKey, in byte[] sealingDicePolicy);
+
+    /*
+     * getKeyslotData() - Gets the keyslot key material referenced by slotId.
+     *
+     * @slotId:
+     *      Identifier for the requested keyslot
+     *
+     * This interface is used to access device specific keys with known types and uses. Because the
+     * returned key is opaque, it can only be used through the different HwCrypto interfaces.
+     * Because the keys live in a global namespace the identity of the caller needs to be
+     * checked to verify that it has permission to accesses the requested key.
+     *
+     * Return:
+     *      Ok(IOpaqueKey) on success, UNAUTHORIZED if the caller cannot access the requested key,
+     *      another specific error code otherwise.
+     */
+    IOpaqueKey getKeyslotData(KeySlot slotId);
 }
diff --git a/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IOpaqueKey.aidl b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IOpaqueKey.aidl
index 0d0f613..9a72639 100644
--- a/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IOpaqueKey.aidl
+++ b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IOpaqueKey.aidl
@@ -16,7 +16,9 @@
 package android.hardware.security.see.hwcrypto;
 
 import android.hardware.security.see.hwcrypto.KeyPolicy;
+import android.hardware.security.see.hwcrypto.types.OpaqueKeyToken;
 import android.hardware.security.see.hwcrypto.types.OperationType;
+import android.hardware.security.see.hwcrypto.types.ProtectionId;
 
 interface IOpaqueKey {
     /*
@@ -52,4 +54,37 @@
      *      <code>HalErrorCode</code> otherwise. Format used for the returned public key is COSE.
      */
     byte[] getPublicKey();
+
+    /*
+     * getShareableToken() - Returns a token that can shared with another HWCrypto client.
+     *
+     * @sealingDicePolicy:
+     *      Token to be used to protect the returned OpaqueKeyToken. It will be used so only
+     *      the owner of the sealingDicePolicy can import the key.
+     * Return:
+     *      <code>OpaqueKeyMaterial</code> token on success, service specific error based on
+     *      <code>HalErrorCode</code> otherwise.
+     */
+    OpaqueKeyToken getShareableToken(in byte[] sealingDicePolicy);
+
+    /*
+     * setProtectionId() - Sets the protectionID associated with the buffers where the operation
+     *                     will be performed. A protection ID serves as a limitation on the key so
+     *                     it can only operate on buffers with a matching protection ID.
+     *                     The client calling this functions needs to have the necessary permissions
+     *                     to read and/or write to this buffer. Setting this parameter means that
+     *                     if the key is shared with a different client, the client receiving the
+     *                     key will be limited in which buffers can be used to read/write data for
+     *                     this operation.
+     *
+     * @protectionId:
+     *      ID of the given use case to provide protection for. The method of protecting the buffer
+     *      will be platform dependent.
+     * @allowedOperations:
+     *      array of allowed operations. Allowed operations are either READ or WRITE.
+     *
+     * Return:
+     *      service specific error based on <code>HalErrorCode</code> on failure.
+     */
+    void setProtectionId(in ProtectionId protectionId, in OperationType[] allowedOperations);
 }
diff --git a/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/OperationParameters.aidl b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/OperationParameters.aidl
index 9e2fc6c..a977f56 100644
--- a/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/OperationParameters.aidl
+++ b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/OperationParameters.aidl
@@ -15,6 +15,7 @@
  */
 package android.hardware.security.see.hwcrypto;
 
+import android.hardware.security.see.hwcrypto.types.HmacOperationParameters;
 import android.hardware.security.see.hwcrypto.types.SymmetricAuthOperationParameters;
 import android.hardware.security.see.hwcrypto.types.SymmetricOperationParameters;
 
@@ -31,4 +32,9 @@
      * Parameters for non-authenticated symmetric cryptography (AES/TDES).
      */
     SymmetricOperationParameters symmetricCrypto;
+
+    /*
+     * Parameters for hash based message authenticated code operations.
+     */
+    HmacOperationParameters hmac;
 }
diff --git a/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/ExplicitKeyMaterial.aidl b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/ExplicitKeyMaterial.aidl
index 4298ba9..3aa5611 100644
--- a/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/ExplicitKeyMaterial.aidl
+++ b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/ExplicitKeyMaterial.aidl
@@ -16,10 +16,12 @@
 package android.hardware.security.see.hwcrypto.types;
 
 import android.hardware.security.see.hwcrypto.types.AesKey;
+import android.hardware.security.see.hwcrypto.types.HmacKey;
 
 /*
  * Type encapsulating a clear key.
  */
 union ExplicitKeyMaterial {
     AesKey aes;
+    HmacKey hmac;
 }
diff --git a/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/HalErrorCode.aidl b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/HalErrorCode.aidl
index e8e8539..f536c0e 100644
--- a/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/HalErrorCode.aidl
+++ b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/HalErrorCode.aidl
@@ -42,4 +42,7 @@
 
     /* Bad parameter supplied for the desired operation */
     const int BAD_PARAMETER = -7;
+
+    /* Caller is not authorized to make this call */
+    const int UNAUTHORIZED = -8;
 }
diff --git a/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/HmacKey.aidl b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/HmacKey.aidl
new file mode 100644
index 0000000..a0b6ba7
--- /dev/null
+++ b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/HmacKey.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.security.see.hwcrypto.types;
+
+/*
+ * Type that represents an Hmac key.
+ */
+union HmacKey {
+    /*
+     * Raw Hmac key for use with sha256.
+     */
+    byte[32] sha256 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0};
+
+    /*
+     * Raw Hmac key for use with sha512.
+     */
+    byte[64] sha512;
+}
diff --git a/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/HmacOperationParameters.aidl b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/HmacOperationParameters.aidl
new file mode 100644
index 0000000..da09a2c
--- /dev/null
+++ b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/HmacOperationParameters.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.security.see.hwcrypto.types;
+
+import android.hardware.security.see.hwcrypto.IOpaqueKey;
+/*
+ * Data needed to perform HMAC operations.
+ */
+parcelable HmacOperationParameters {
+    /*
+     * Key to be used for the HMAC operation.
+     */
+    IOpaqueKey key;
+}
diff --git a/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/OpaqueKeyToken.aidl b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/OpaqueKeyToken.aidl
new file mode 100644
index 0000000..db95c18
--- /dev/null
+++ b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/OpaqueKeyToken.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.security.see.hwcrypto.types;
+
+/*
+ * Implementation defined structure that represents a key and its associated metadata. It is only
+ * valid on the current boot, and its reuse after a session is closed (or between sessions) is not
+ * guaranteed.
+ */
+parcelable OpaqueKeyToken {
+    /*
+     * Opaque type used to send IOpaqueKeys keys to different clients. Its format is implementation
+     * dependant.
+     */
+    byte[] keyToken;
+}
diff --git a/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/ProtectionId.aidl b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/ProtectionId.aidl
new file mode 100644
index 0000000..8686882
--- /dev/null
+++ b/staging/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/types/ProtectionId.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.security.see.hwcrypto.types;
+
+/*
+ * Enum describing the different types of protected buffers. Protected buffers are named by its
+ * corresponding use case and its underlaying implementation is platform dependant.
+ */
+enum ProtectionId {
+    /*
+     * ProtectionID used by HwCrypto to enable Keys that can be used for Widevine video buffers.
+     * These buffers should not be readable by non-trusted entities and HwCrypto should not allow
+     * any read access to them through its interface.
+     */
+    WIDEVINE_OUTPUT_BUFFER = 1,
+}
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 b617c57..e96b386 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
@@ -86,6 +86,6 @@
   enum MloLinkInfoChangeReason {
     TID_TO_LINK_MAP = 0,
     MULTI_LINK_RECONFIG_AP_REMOVAL = 1,
-    MULTI_LINK_RECONFIG_AP_ADDITION = 2,
+    MULTI_LINK_DYNAMIC_RECONFIG = 2,
   }
 }
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
index 8740ad0..e5e2e84 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
@@ -336,21 +336,24 @@
          */
         TID_TO_LINK_MAP = 0,
         /**
-         * Multi-link reconfiguration - AP removal as described in
-         * IEEE 802.11be spec, section 35.3.6. This is a mandatory feature for
-         * station according to Wi-Fi 7 R1 MRD.
+         * Multi-link reconfiguration - AP removal as described in the Wi-Fi 7 R1 MRD section
+         * 6.3.2.18. This is a mandatory feature for station.
          *
          * Removed link will not be present in |ISupplicantStaIface.getConnectionMloLinksInfo|.
          */
         MULTI_LINK_RECONFIG_AP_REMOVAL = 1,
         /**
-         * Multi-link reconfiguration - Adding affiliated AP(s) as described in
-         * IEEE 802.11be spec, section 35.3.6. This is an optional feature for
-         * station according to Wi-Fi 7 R2 MRD.
+         * Multi-link reconfiguration add/delete links without re-association as described in
+         * the Wi-Fi 7 R2 MRD section 6.4.2.4. This is an optional feature.
+         *
+         * This feature enables dynamic link reconfiguration operations (add link and/or delete
+         * link) on the multi-link setup of a STA MLD, either triggered by the AP MLD or initiated
+         * by the STA MLD itself. This avoids reassociation for any link reconfiguration operation.
          *
          * Added link will be present in |ISupplicantStaIface.getConnectionMloLinksInfo|.
+         * Deleted link will not be present in |ISupplicantStaIface.getConnectionMloLinksInfo|.
          */
-        MULTI_LINK_RECONFIG_AP_ADDITION = 2,
+        MULTI_LINK_DYNAMIC_RECONFIG = 2,
     }
 
     /**