Merge "Camera: Add physical camera crop metadata tag" into main
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl
index bcbf870..046c220 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl
@@ -43,6 +43,8 @@
   boolean audioModeIndication;
   boolean audioSourceIndication;
   boolean bypass;
+  boolean sinkMetadataIndication;
+  boolean sourceMetadataIndication;
   @Backing(type="byte") @VintfStability
   enum Type {
     INSERT = 0,
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
index 7313b57..ff33c42 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
@@ -41,6 +41,8 @@
   boolean offload;
   android.hardware.audio.effect.Parameter.VolumeStereo volumeStereo;
   android.hardware.audio.effect.Parameter.Specific specific;
+  android.hardware.audio.common.SinkMetadata sinkMetadata;
+  android.hardware.audio.common.SourceMetadata sourceMetadata;
   @VintfStability
   union Id {
     android.hardware.audio.effect.VendorExtension vendorEffectTag;
diff --git a/audio/aidl/android/hardware/audio/effect/Flags.aidl b/audio/aidl/android/hardware/audio/effect/Flags.aidl
index 28685c3..70668a3 100644
--- a/audio/aidl/android/hardware/audio/effect/Flags.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Flags.aidl
@@ -144,4 +144,18 @@
      * Set to true if the effect instance bypass audio data (no processing).
      */
     boolean bypass;
+
+    /**
+     * Effect instance sets this flag to true if it requires record AudioTrack metadata update. In
+     * this case the framework must call IEffect.setParameter to notify effect instance when there
+     * is a change in sinkMetadata.
+     */
+    boolean sinkMetadataIndication;
+
+    /**
+     * Effect instance sets this flag to true if it requires playback AudioTrack metadata update. In
+     * this case the framework must call IEffect.setParameter to notify effect instance when there
+     * is a change in sourceMetadata.
+     */
+    boolean sourceMetadataIndication;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/android/hardware/audio/effect/Parameter.aidl
index 6ec7226..6fd9161 100644
--- a/audio/aidl/android/hardware/audio/effect/Parameter.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Parameter.aidl
@@ -16,6 +16,8 @@
 
 package android.hardware.audio.effect;
 
+import android.hardware.audio.common.SinkMetadata;
+import android.hardware.audio.common.SourceMetadata;
 import android.hardware.audio.effect.AcousticEchoCanceler;
 import android.hardware.audio.effect.AutomaticGainControlV1;
 import android.hardware.audio.effect.AutomaticGainControlV2;
@@ -198,4 +200,20 @@
         Spatializer spatializer;
     }
     Specific specific;
+
+    /**
+     * SinkMetadata defines the metadata of record AudioTracks which the effect instance associate
+     * with.
+     * The effect engine is required to set Flags.sinkMetadataIndication to true if it wants to
+     * receive sinkMetadata update from the audio framework.
+     */
+    SinkMetadata sinkMetadata;
+
+    /**
+     * SourceMetadata defines the metadata of playback AudioTracks which the effect instance
+     * associate with.
+     * The effect engine is required to set Flags.sourceMetadataIndication to true if it wants to
+     * receive sourceMetadata update from the audio framework.
+     */
+    SourceMetadata sourceMetadata;
 }
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 11bd7d3..7cd0545 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -94,6 +94,7 @@
         "audio_policy_engine_configuration_aidl_default",
     ],
     shared_libs: [
+        "android.hardware.bluetooth.audio-impl",
         "libaudio_aidl_conversion_common_ndk",
         "libaudioutils",
         "libbluetooth_audio_session_aidl",
@@ -130,6 +131,7 @@
         "libaudioserviceexampleimpl",
     ],
     shared_libs: [
+        "android.hardware.bluetooth.audio-impl",
         "libaudio_aidl_conversion_common_ndk",
         "libbluetooth_audio_session_aidl",
         "liblog",
diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp
index 254eb46..d63e353 100644
--- a/audio/aidl/default/Configuration.cpp
+++ b/audio/aidl/default/Configuration.cpp
@@ -320,9 +320,9 @@
 //    - no profiles specified
 //
 // Mix ports:
-//  * "r_submix output", maximum 20 opened streams, maximum 10 active streams
+//  * "r_submix output", maximum 10 opened streams, maximum 10 active streams
 //    - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
-//  * "r_submix input", maximum 20 opened streams, maximum 10 active streams
+//  * "r_submix input", maximum 10 opened streams, maximum 10 active streams
 //    - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
 //
 // Routes:
@@ -355,12 +355,12 @@
         // Mix ports
 
         AudioPort rsubmixOutMix =
-                createPort(c.nextPortId++, "r_submix output", 0, false, createPortMixExt(20, 10));
+                createPort(c.nextPortId++, "r_submix output", 0, false, createPortMixExt(10, 10));
         rsubmixOutMix.profiles = remoteSubmixPcmAudioProfiles;
         c.ports.push_back(rsubmixOutMix);
 
         AudioPort rsubmixInMix =
-                createPort(c.nextPortId++, "r_submix input", 0, true, createPortMixExt(20, 10));
+                createPort(c.nextPortId++, "r_submix input", 0, true, createPortMixExt(10, 10));
         rsubmixInMix.profiles = remoteSubmixPcmAudioProfiles;
         c.ports.push_back(rsubmixInMix);
 
diff --git a/audio/aidl/default/XsdcConversion.cpp b/audio/aidl/default/XsdcConversion.cpp
index 9e30347..1720949 100644
--- a/audio/aidl/default/XsdcConversion.cpp
+++ b/audio/aidl/default/XsdcConversion.cpp
@@ -205,24 +205,28 @@
 ConversionResult<AudioIoFlags> convertIoFlagsToAidl(
         const std::vector<ap_xsd::AudioInOutFlag>& flags, const ap_xsd::Role role,
         bool flagsForMixPort) {
-    int flagMask = 0;
+    int legacyFlagMask = 0;
     if ((role == ap_xsd::Role::sink && flagsForMixPort) ||
         (role == ap_xsd::Role::source && !flagsForMixPort)) {
         for (const ap_xsd::AudioInOutFlag& flag : flags) {
             audio_input_flags_t legacyFlag;
             if (::android::InputFlagConverter::fromString(ap_xsd::toString(flag), legacyFlag)) {
-                flagMask |= static_cast<int>(legacyFlag);
+                legacyFlagMask |= static_cast<int>(legacyFlag);
             }
         }
-        return AudioIoFlags::make<AudioIoFlags::Tag::input>(flagMask);
+        return AudioIoFlags::make<AudioIoFlags::Tag::input>(
+                VALUE_OR_FATAL(legacy2aidl_audio_input_flags_t_int32_t_mask(
+                        static_cast<audio_input_flags_t>(legacyFlagMask))));
     } else {
         for (const ap_xsd::AudioInOutFlag& flag : flags) {
             audio_output_flags_t legacyFlag;
             if (::android::OutputFlagConverter::fromString(ap_xsd::toString(flag), legacyFlag)) {
-                flagMask |= static_cast<int>(legacyFlag);
+                legacyFlagMask |= static_cast<int>(legacyFlag);
             }
         }
-        return AudioIoFlags::make<AudioIoFlags::Tag::output>(flagMask);
+        return AudioIoFlags::make<AudioIoFlags::Tag::output>(
+                VALUE_OR_FATAL(legacy2aidl_audio_output_flags_t_int32_t_mask(
+                        static_cast<audio_output_flags_t>(legacyFlagMask))));
     }
 }
 
diff --git a/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp b/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp
index 8727232..5e18f1b 100644
--- a/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp
+++ b/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp
@@ -76,7 +76,7 @@
                           .proxy = std::nullopt},
                    .flags = {.type = Flags::Type::PRE_PROC,
                              .insert = Flags::Insert::FIRST,
-                             .volume = Flags::Volume::CTRL},
+                             .volume = Flags::Volume::NONE},
                    .name = AcousticEchoCancelerSw::kEffectName,
                    .implementor = "The Android Open Source Project"},
         .capability = AcousticEchoCancelerSw::kCapability};
diff --git a/audio/aidl/default/bluetooth/ModuleBluetooth.cpp b/audio/aidl/default/bluetooth/ModuleBluetooth.cpp
index 502b153..8a1cbbf 100644
--- a/audio/aidl/default/bluetooth/ModuleBluetooth.cpp
+++ b/audio/aidl/default/bluetooth/ModuleBluetooth.cpp
@@ -33,8 +33,23 @@
 using android::bluetooth::audio::aidl::BluetoothAudioPortAidl;
 using android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut;
 
+// TODO(b/312265159) bluetooth audio should be in its own process
+// Remove this and the shared_libs when that happens
+extern "C" binder_status_t createIBluetoothAudioProviderFactory();
+
 namespace aidl::android::hardware::audio::core {
 
+ModuleBluetooth::ModuleBluetooth(std::unique_ptr<Module::Configuration>&& config)
+    : Module(Type::BLUETOOTH, std::move(config)) {
+    // TODO(b/312265159) bluetooth audio should be in its own process
+    // Remove this and the shared_libs when that happens
+    binder_status_t status = createIBluetoothAudioProviderFactory();
+    if (status != STATUS_OK) {
+        LOG(ERROR) << "Failed to create bluetooth audio provider factory. Status: "
+                   << ::android::statusToString(status);
+    }
+}
+
 ndk::ScopedAStatus ModuleBluetooth::getBluetoothA2dp(
         std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
     *_aidl_return = getBtA2dp().getInstance();
diff --git a/audio/aidl/default/include/core-impl/ModuleBluetooth.h b/audio/aidl/default/include/core-impl/ModuleBluetooth.h
index a58798b..631b088 100644
--- a/audio/aidl/default/include/core-impl/ModuleBluetooth.h
+++ b/audio/aidl/default/include/core-impl/ModuleBluetooth.h
@@ -28,8 +28,7 @@
                        std::weak_ptr<IBluetoothLe>>
             BtProfileHandles;
 
-    ModuleBluetooth(std::unique_ptr<Configuration>&& config)
-        : Module(Type::BLUETOOTH, std::move(config)) {}
+    ModuleBluetooth(std::unique_ptr<Configuration>&& config);
 
   private:
     ChildInterface<BluetoothA2dp>& getBtA2dp();
diff --git a/audio/aidl/default/include/core-impl/StreamPrimary.h b/audio/aidl/default/include/core-impl/StreamPrimary.h
index abc119c..145c3c4 100644
--- a/audio/aidl/default/include/core-impl/StreamPrimary.h
+++ b/audio/aidl/default/include/core-impl/StreamPrimary.h
@@ -27,13 +27,18 @@
   public:
     StreamPrimary(StreamContext* context, const Metadata& metadata);
 
+    ::android::status_t start() override;
     ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                  int32_t* latencyMs) override;
+    ::android::status_t refinePosition(StreamDescriptor::Position* position) override;
 
   protected:
     std::vector<alsa::DeviceProfile> getDeviceProfiles() override;
 
     const bool mIsAsynchronous;
+    long mStartTimeNs = 0;
+    long mFramesSinceStart = 0;
+    bool mSkipNextTransfer = false;
 };
 
 class StreamInPrimary final : public StreamIn, public StreamSwitcher, public StreamInHwGainHelper {
diff --git a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
index 21592b3..ee10abf 100644
--- a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
+++ b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
@@ -71,6 +71,10 @@
     static constexpr int kMaxReadFailureAttempts = 3;
     // 5ms between two read attempts when pipe is empty
     static constexpr int kReadAttemptSleepUs = 5000;
+
+    long mStartTimeNs = 0;
+    long mFramesSinceStart = 0;
+    int mReadErrorCount = 0;
 };
 
 class StreamInRemoteSubmix final : public StreamIn, public StreamSwitcher {
diff --git a/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp b/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp
index 99f2caf..a3208df 100644
--- a/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp
+++ b/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp
@@ -67,7 +67,7 @@
                           .proxy = std::nullopt},
                    .flags = {.type = Flags::Type::PRE_PROC,
                              .insert = Flags::Insert::FIRST,
-                             .volume = Flags::Volume::CTRL},
+                             .volume = Flags::Volume::NONE},
                    .name = NoiseSuppressionSw::kEffectName,
                    .implementor = "The Android Open Source Project"}};
 
diff --git a/audio/aidl/default/primary/StreamPrimary.cpp b/audio/aidl/default/primary/StreamPrimary.cpp
index 7e3bdd4..b22ef32 100644
--- a/audio/aidl/default/primary/StreamPrimary.cpp
+++ b/audio/aidl/default/primary/StreamPrimary.cpp
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-#include <chrono>
-
 #define LOG_TAG "AHAL_StreamPrimary"
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <audio_utils/clock.h>
+#include <error/Result.h>
 #include <error/expected_utils.h>
 
 #include "PrimaryMixer.h"
@@ -43,26 +42,52 @@
     context->startStreamDataProcessor();
 }
 
+::android::status_t StreamPrimary::start() {
+    RETURN_STATUS_IF_ERROR(StreamAlsa::start());
+    mStartTimeNs = ::android::uptimeNanos();
+    mFramesSinceStart = 0;
+    mSkipNextTransfer = false;
+    return ::android::OK;
+}
+
 ::android::status_t StreamPrimary::transfer(void* buffer, size_t frameCount,
                                             size_t* actualFrameCount, int32_t* latencyMs) {
-    auto start = std::chrono::steady_clock::now();
-    if (auto status = StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs);
-        status != ::android::OK) {
-        return status;
-    }
     // This is a workaround for the emulator implementation which has a host-side buffer
-    // and this can result in reading faster than real time.
-    if (mIsInput && !mIsAsynchronous) {
-        auto recordDurationUs = std::chrono::duration_cast<std::chrono::microseconds>(
-                std::chrono::steady_clock::now() - start);
-        const long projectedVsObservedOffsetUs =
-                *actualFrameCount * MICROS_PER_SECOND / mContext.getSampleRate() -
-                recordDurationUs.count();
-        if (projectedVsObservedOffsetUs > 0) {
-            LOG(VERBOSE) << __func__ << ": sleeping for " << projectedVsObservedOffsetUs << " us";
-            usleep(projectedVsObservedOffsetUs);
-        }
+    // and is not being able to achieve real-time behavior similar to ADSPs (b/302587331).
+    if (!mSkipNextTransfer) {
+        RETURN_STATUS_IF_ERROR(
+                StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs));
+    } else {
+        LOG(DEBUG) << __func__ << ": skipping transfer (" << frameCount << " frames)";
+        *actualFrameCount = frameCount;
+        if (mIsInput) memset(buffer, 0, frameCount * mFrameSizeBytes);
+        mSkipNextTransfer = false;
     }
+    if (!mIsAsynchronous) {
+        const long bufferDurationUs =
+                (*actualFrameCount) * MICROS_PER_SECOND / mContext.getSampleRate();
+        const auto totalDurationUs =
+                (::android::uptimeNanos() - mStartTimeNs) / NANOS_PER_MICROSECOND;
+        mFramesSinceStart += *actualFrameCount;
+        const long totalOffsetUs =
+                mFramesSinceStart * MICROS_PER_SECOND / mContext.getSampleRate() - totalDurationUs;
+        LOG(VERBOSE) << __func__ << ": totalOffsetUs " << totalOffsetUs;
+        if (totalOffsetUs > 0) {
+            const long sleepTimeUs = std::min(totalOffsetUs, bufferDurationUs);
+            LOG(VERBOSE) << __func__ << ": sleeping for " << sleepTimeUs << " us";
+            usleep(sleepTimeUs);
+        } else {
+            mSkipNextTransfer = true;
+        }
+    } else {
+        LOG(VERBOSE) << __func__ << ": asynchronous transfer";
+    }
+    return ::android::OK;
+}
+
+::android::status_t StreamPrimary::refinePosition(StreamDescriptor::Position*) {
+    // Since not all data is actually sent to the HAL, use the position maintained by Stream class
+    // which accounts for all frames passed from / to the client.
     return ::android::OK;
 }
 
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index fc61dcb..6258c93 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -16,6 +16,9 @@
 
 #define LOG_TAG "AHAL_StreamRemoteSubmix"
 #include <android-base/logging.h>
+#include <audio_utils/clock.h>
+#include <error/Result.h>
+#include <error/expected_utils.h>
 
 #include "core-impl/StreamRemoteSubmix.h"
 
@@ -50,37 +53,33 @@
         if (routeItr != sSubmixRoutes.end()) {
             mCurrentRoute = routeItr->second;
         }
-    }
-    // If route is not available for this port, add it.
-    if (mCurrentRoute == nullptr) {
-        // Initialize the pipe.
-        mCurrentRoute = std::make_shared<SubmixRoute>();
-        if (::android::OK != mCurrentRoute->createPipe(mStreamConfig)) {
-            LOG(ERROR) << __func__ << ": create pipe failed";
-            return ::android::NO_INIT;
-        }
-        {
-            std::lock_guard guard(sSubmixRoutesLock);
-            sSubmixRoutes.emplace(mDeviceAddress, mCurrentRoute);
-        }
-    } else {
-        if (!mCurrentRoute->isStreamConfigValid(mIsInput, mStreamConfig)) {
-            LOG(ERROR) << __func__ << ": invalid stream config";
-            return ::android::NO_INIT;
-        }
-        sp<MonoPipe> sink = mCurrentRoute->getSink();
-        if (sink == nullptr) {
-            LOG(ERROR) << __func__ << ": nullptr sink when opening stream";
-            return ::android::NO_INIT;
-        }
-        // If the sink has been shutdown or pipe recreation is forced, delete the pipe and
-        // recreate it.
-        if (sink->isShutdown()) {
-            LOG(DEBUG) << __func__ << ": Non-nullptr shut down sink when opening stream";
-            if (::android::OK != mCurrentRoute->resetPipe()) {
-                LOG(ERROR) << __func__ << ": reset pipe failed";
+        // If route is not available for this port, add it.
+        if (mCurrentRoute == nullptr) {
+            // Initialize the pipe.
+            mCurrentRoute = std::make_shared<SubmixRoute>();
+            if (::android::OK != mCurrentRoute->createPipe(mStreamConfig)) {
+                LOG(ERROR) << __func__ << ": create pipe failed";
                 return ::android::NO_INIT;
             }
+            sSubmixRoutes.emplace(mDeviceAddress, mCurrentRoute);
+        }
+    }
+    if (!mCurrentRoute->isStreamConfigValid(mIsInput, mStreamConfig)) {
+        LOG(ERROR) << __func__ << ": invalid stream config";
+        return ::android::NO_INIT;
+    }
+    sp<MonoPipe> sink = mCurrentRoute->getSink();
+    if (sink == nullptr) {
+        LOG(ERROR) << __func__ << ": nullptr sink when opening stream";
+        return ::android::NO_INIT;
+    }
+    // If the sink has been shutdown or pipe recreation is forced, delete the pipe and
+    // recreate it.
+    if (sink->isShutdown()) {
+        LOG(DEBUG) << __func__ << ": Non-nullptr shut down sink when opening stream";
+        if (::android::OK != mCurrentRoute->resetPipe()) {
+            LOG(ERROR) << __func__ << ": reset pipe failed";
+            return ::android::NO_INIT;
         }
     }
 
@@ -110,6 +109,8 @@
 
 ::android::status_t StreamRemoteSubmix::start() {
     mCurrentRoute->exitStandby(mIsInput);
+    mStartTimeNs = ::android::uptimeNanos();
+    mFramesSinceStart = 0;
     return ::android::OK;
 }
 
@@ -161,8 +162,21 @@
     *latencyMs = getDelayInUsForFrameCount(getStreamPipeSizeInFrames()) / 1000;
     LOG(VERBOSE) << __func__ << ": Latency " << *latencyMs << "ms";
     mCurrentRoute->exitStandby(mIsInput);
-    return (mIsInput ? inRead(buffer, frameCount, actualFrameCount)
-                     : outWrite(buffer, frameCount, actualFrameCount));
+    RETURN_STATUS_IF_ERROR(mIsInput ? inRead(buffer, frameCount, actualFrameCount)
+                                    : outWrite(buffer, frameCount, actualFrameCount));
+    const long bufferDurationUs =
+            (*actualFrameCount) * MICROS_PER_SECOND / mContext.getSampleRate();
+    const long totalDurationUs = (::android::uptimeNanos() - mStartTimeNs) / NANOS_PER_MICROSECOND;
+    mFramesSinceStart += *actualFrameCount;
+    const long totalOffsetUs =
+            mFramesSinceStart * MICROS_PER_SECOND / mContext.getSampleRate() - totalDurationUs;
+    LOG(VERBOSE) << __func__ << ": totalOffsetUs " << totalOffsetUs;
+    if (totalOffsetUs > 0) {
+        const long sleepTimeUs = std::min(totalOffsetUs, bufferDurationUs);
+        LOG(VERBOSE) << __func__ << ": sleeping for " << sleepTimeUs << " us";
+        usleep(sleepTimeUs);
+    }
+    return ::android::OK;
 }
 
 ::android::status_t StreamRemoteSubmix::refinePosition(StreamDescriptor::Position* position) {
@@ -200,12 +214,7 @@
     if (sink != nullptr) {
         if (sink->isShutdown()) {
             sink.clear();
-            const auto delayUs = getDelayInUsForFrameCount(frameCount);
-            LOG(DEBUG) << __func__ << ": pipe shutdown, ignoring the write, sleeping for "
-                       << delayUs << " us";
-            // the pipe has already been shutdown, this buffer will be lost but we must
-            // simulate timing so we don't drain the output faster than realtime
-            usleep(delayUs);
+            LOG(DEBUG) << __func__ << ": pipe shutdown, ignoring the write";
             *actualFrameCount = frameCount;
             return ::android::OK;
         }
@@ -214,6 +223,9 @@
         return ::android::UNKNOWN_ERROR;
     }
 
+    LOG(VERBOSE) << __func__ << ": " << mDeviceAddress.toString() << ", " << frameCount
+                 << " frames";
+
     const bool shouldBlockWrite = mCurrentRoute->shouldBlockWrite();
     size_t availableToWrite = sink->availableToWrite();
     // NOTE: sink has been checked above and sink and source life cycles are synchronized
@@ -236,6 +248,8 @@
     availableToWrite = sink->availableToWrite();
 
     if (!shouldBlockWrite && frameCount > availableToWrite) {
+        LOG(WARNING) << __func__ << ": writing " << availableToWrite << " vs. requested "
+                     << frameCount;
         // Truncate the request to avoid blocking.
         frameCount = availableToWrite;
     }
@@ -258,92 +272,59 @@
         *actualFrameCount = 0;
         return ::android::UNKNOWN_ERROR;
     }
-    LOG(VERBOSE) << __func__ << ": wrote " << writtenFrames << "frames";
+    if (writtenFrames > 0 && frameCount > (size_t)writtenFrames) {
+        LOG(WARNING) << __func__ << ": wrote " << writtenFrames << " vs. requested " << frameCount;
+    }
     *actualFrameCount = writtenFrames;
     return ::android::OK;
 }
 
 ::android::status_t StreamRemoteSubmix::inRead(void* buffer, size_t frameCount,
                                                size_t* actualFrameCount) {
+    // in any case, it is emulated that data for the entire buffer was available
+    memset(buffer, 0, mStreamConfig.frameSize * frameCount);
+    *actualFrameCount = frameCount;
+
     // about to read from audio source
     sp<MonoPipeReader> source = mCurrentRoute->getSource();
     if (source == nullptr) {
-        int readErrorCount = mCurrentRoute->notifyReadError();
-        if (readErrorCount < kMaxReadErrorLogs) {
+        if (++mReadErrorCount < kMaxReadErrorLogs) {
             LOG(ERROR) << __func__
                        << ": no audio pipe yet we're trying to read! (not all errors will be "
                           "logged)";
-        } else {
-            LOG(ERROR) << __func__ << ": Read errors " << readErrorCount;
         }
-        const auto delayUs = getDelayInUsForFrameCount(frameCount);
-        LOG(DEBUG) << __func__ << ": no source, ignoring the read, sleeping for " << delayUs
-                   << " us";
-        usleep(delayUs);
-        memset(buffer, 0, mStreamConfig.frameSize * frameCount);
-        *actualFrameCount = frameCount;
         return ::android::OK;
     }
 
+    LOG(VERBOSE) << __func__ << ": " << mDeviceAddress.toString() << ", " << frameCount
+                 << " frames";
     // read the data from the pipe
-    int attempts = 0;
-    const long delayUs = kReadAttemptSleepUs;
     char* buff = (char*)buffer;
-    size_t remainingFrames = frameCount;
-    int availableToRead = source->availableToRead();
-
-    while ((remainingFrames > 0) && (availableToRead > 0) && (attempts < kMaxReadFailureAttempts)) {
-        LOG(VERBOSE) << __func__ << ": frames available to read " << availableToRead;
-
+    size_t actuallyRead = 0;
+    long remainingFrames = frameCount;
+    const long deadlineTimeNs = ::android::uptimeNanos() +
+                                getDelayInUsForFrameCount(frameCount) * NANOS_PER_MICROSECOND;
+    while (remainingFrames > 0) {
         ssize_t framesRead = source->read(buff, remainingFrames);
-
         LOG(VERBOSE) << __func__ << ": frames read " << framesRead;
-
         if (framesRead > 0) {
             remainingFrames -= framesRead;
             buff += framesRead * mStreamConfig.frameSize;
-            availableToRead -= framesRead;
-            LOG(VERBOSE) << __func__ << ": (attempts = " << attempts << ") got " << framesRead
+            LOG(VERBOSE) << __func__ << ": got " << framesRead
                          << " frames, remaining =" << remainingFrames;
-        } else {
-            attempts++;
-            LOG(WARNING) << __func__ << ": read returned " << framesRead
-                         << " , read failure attempts = " << attempts << ", sleeping for "
-                         << delayUs << " us";
-            usleep(delayUs);
+            actuallyRead += framesRead;
+        }
+        if (::android::uptimeNanos() >= deadlineTimeNs) break;
+        if (framesRead <= 0) {
+            LOG(VERBOSE) << __func__ << ": read returned " << framesRead
+                         << ", read failure, sleeping for " << kReadAttemptSleepUs << " us";
+            usleep(kReadAttemptSleepUs);
         }
     }
-    // done using the source
-    source.clear();
-
-    if (remainingFrames > 0) {
-        const size_t remainingBytes = remainingFrames * mStreamConfig.frameSize;
-        LOG(VERBOSE) << __func__ << ": clearing remaining_frames = " << remainingFrames;
-        memset(((char*)buffer) + (mStreamConfig.frameSize * frameCount) - remainingBytes, 0,
-               remainingBytes);
+    if (actuallyRead < frameCount) {
+        LOG(WARNING) << __func__ << ": read " << actuallyRead << " vs. requested " << frameCount;
     }
-
-    long readCounterFrames = mCurrentRoute->updateReadCounterFrames(frameCount);
-    *actualFrameCount = frameCount;
-
-    // compute how much we need to sleep after reading the data by comparing the wall clock with
-    //   the projected time at which we should return.
-    // wall clock after reading from the pipe
-    auto recordDurationUs = std::chrono::duration_cast<std::chrono::microseconds>(
-            std::chrono::steady_clock::now() - mCurrentRoute->getRecordStartTime());
-
-    // readCounterFrames contains the number of frames that have been read since the beginning of
-    // recording (including this call): it's converted to usec and compared to how long we've been
-    // recording for, which gives us how long we must wait to sync the projected recording time, and
-    // the observed recording time.
-    const long projectedVsObservedOffsetUs =
-            getDelayInUsForFrameCount(readCounterFrames) - recordDurationUs.count();
-
-    LOG(VERBOSE) << __func__ << ": record duration " << recordDurationUs.count()
-                 << " us, will wait: " << projectedVsObservedOffsetUs << " us";
-    if (projectedVsObservedOffsetUs > 0) {
-        usleep(projectedVsObservedOffsetUs);
-    }
+    mCurrentRoute->updateReadCounterFrames(*actualFrameCount);
     return ::android::OK;
 }
 
diff --git a/audio/aidl/default/r_submix/SubmixRoute.cpp b/audio/aidl/default/r_submix/SubmixRoute.cpp
index ddac64d..f04e607 100644
--- a/audio/aidl/default/r_submix/SubmixRoute.cpp
+++ b/audio/aidl/default/r_submix/SubmixRoute.cpp
@@ -81,11 +81,6 @@
     return (mStreamInOpen || (mStreamInStandby && (mReadCounterFrames != 0)));
 }
 
-int SubmixRoute::notifyReadError() {
-    std::lock_guard guard(mLock);
-    return ++mReadErrorCount;
-}
-
 long SubmixRoute::updateReadCounterFrames(size_t frameCount) {
     std::lock_guard guard(mLock);
     mReadCounterFrames += frameCount;
@@ -103,7 +98,6 @@
         }
         mStreamInStandby = true;
         mReadCounterFrames = 0;
-        mReadErrorCount = 0;
     } else {
         mStreamOutOpen = true;
     }
@@ -214,9 +208,6 @@
         if (mStreamInStandby || mStreamOutStandbyTransition) {
             mStreamInStandby = false;
             mStreamOutStandbyTransition = false;
-            // keep track of when we exit input standby (== first read == start "real recording")
-            // or when we start recording silence, and reset projected time
-            mRecordStartTime = std::chrono::steady_clock::now();
             mReadCounterFrames = 0;
         }
     } else {
diff --git a/audio/aidl/default/r_submix/SubmixRoute.h b/audio/aidl/default/r_submix/SubmixRoute.h
index 92b95e9..252b1c9 100644
--- a/audio/aidl/default/r_submix/SubmixRoute.h
+++ b/audio/aidl/default/r_submix/SubmixRoute.h
@@ -16,7 +16,6 @@
 
 #pragma once
 
-#include <chrono>
 #include <mutex>
 
 #include <android-base/thread_annotations.h>
@@ -83,14 +82,6 @@
         std::lock_guard guard(mLock);
         return mReadCounterFrames;
     }
-    int getReadErrorCount() {
-        std::lock_guard guard(mLock);
-        return mReadErrorCount;
-    }
-    std::chrono::time_point<std::chrono::steady_clock> getRecordStartTime() {
-        std::lock_guard guard(mLock);
-        return mRecordStartTime;
-    }
     sp<MonoPipe> getSink() {
         std::lock_guard guard(mLock);
         return mSink;
@@ -126,9 +117,6 @@
     bool mStreamOutStandby GUARDED_BY(mLock) = true;
     // how many frames have been requested to be read since standby
     long mReadCounterFrames GUARDED_BY(mLock) = 0;
-    int mReadErrorCount GUARDED_BY(mLock) = 0;
-    // wall clock when recording starts
-    std::chrono::time_point<std::chrono::steady_clock> mRecordStartTime GUARDED_BY(mLock);
 
     // Pipe variables: they handle the ring buffer that "pipes" audio:
     //  - from the submix virtual audio output == what needs to be played
diff --git a/audio/aidl/default/visualizer/VisualizerSw.cpp b/audio/aidl/default/visualizer/VisualizerSw.cpp
index 0909f25..285c102 100644
--- a/audio/aidl/default/visualizer/VisualizerSw.cpp
+++ b/audio/aidl/default/visualizer/VisualizerSw.cpp
@@ -73,7 +73,7 @@
                           .proxy = std::nullopt},
                    .flags = {.type = Flags::Type::INSERT,
                              .insert = Flags::Insert::FIRST,
-                             .volume = Flags::Volume::CTRL},
+                             .volume = Flags::Volume::NONE},
                    .name = VisualizerSw::kEffectName,
                    .implementor = "The Android Open Source Project"},
         .capability = VisualizerSw::kCapability};
diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
index ca1cea9..aaf9ad4 100644
--- a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
@@ -596,8 +596,14 @@
 
     Parameter::Id id = Parameter::Id::make<Parameter::Id::commonTag>(Parameter::volumeStereo);
     Parameter::VolumeStereo volume = {.left = 10.0, .right = 10.0};
-    ASSERT_NO_FATAL_FAILURE(
-            setAndGetParameter(id, Parameter::make<Parameter::volumeStereo>(volume)));
+    if (mDescriptor.common.flags.volume == Flags::Volume::CTRL) {
+        Parameter get;
+        EXPECT_IS_OK(mEffect->setParameter(volume));
+        EXPECT_IS_OK(mEffect->getParameter(id, &get));
+    } else {
+        ASSERT_NO_FATAL_FAILURE(
+                setAndGetParameter(id, Parameter::make<Parameter::volumeStereo>(volume)));
+    }
 
     ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::STOP));
     ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE));
diff --git a/audio/effect/4.0/xml/Android.bp b/audio/effect/4.0/xml/Android.bp
index 8c03a35..bdffe60 100644
--- a/audio/effect/4.0/xml/Android.bp
+++ b/audio/effect/4.0/xml/Android.bp
@@ -9,9 +9,9 @@
 
 genrule {
     name: "audio_effects_conf_V4_0",
-    srcs: ["audio_effects_conf.xsd"],
+    srcs: [":audio_effects_conf_V2_0"],
     out: [
         "audio_effects_conf_V4_0.xsd",
     ],
-    cmd: "cp -f $(in) $(genDir)/audio_effects_conf_V4_0.xsd",
+    cmd: "cp -f $(in) $(out)",
 }
diff --git a/audio/effect/4.0/xml/audio_effects_conf.xsd b/audio/effect/4.0/xml/audio_effects_conf.xsd
deleted file mode 120000
index 9d85fa7..0000000
--- a/audio/effect/4.0/xml/audio_effects_conf.xsd
+++ /dev/null
@@ -1 +0,0 @@
-../../2.0/xml/audio_effects_conf.xsd
\ No newline at end of file
diff --git a/audio/effect/5.0/xml/Android.bp b/audio/effect/5.0/xml/Android.bp
index 7982e2a..ed12e38 100644
--- a/audio/effect/5.0/xml/Android.bp
+++ b/audio/effect/5.0/xml/Android.bp
@@ -9,6 +9,6 @@
 
 xsd_config {
     name: "audio_effects_conf_V5_0",
-    srcs: ["audio_effects_conf.xsd"],
+    srcs: [":audio_effects_conf_V2_0"],
     package_name: "audio.effects.V5_0",
 }
diff --git a/audio/effect/5.0/xml/audio_effects_conf.xsd b/audio/effect/5.0/xml/audio_effects_conf.xsd
deleted file mode 120000
index 9d85fa7..0000000
--- a/audio/effect/5.0/xml/audio_effects_conf.xsd
+++ /dev/null
@@ -1 +0,0 @@
-../../2.0/xml/audio_effects_conf.xsd
\ No newline at end of file
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/RecurrentTimer.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/RecurrentTimer.h
index 0ed8742..0f5987e 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/RecurrentTimer.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/RecurrentTimer.h
@@ -122,21 +122,29 @@
             }
 
             std::unique_lock<std::mutex> g(mLock);
+            // mStopRequested might be set to true after we enter the loop. Must check inside
+            // the lock to make sure the value will not change before we start the wait.
+            if (mStopRequested) {
+                return;
+            }
             mCond.wait_until(g, nextEventTime);  // nextEventTime can be nanoseconds::max()
         }
     }
 
     void stop() {
-        mStopRequested = true;
         {
             std::lock_guard<std::mutex> g(mLock);
             mCookieToEventsMap.clear();
+            // Even though this is atomic, this must be set inside the lock to make sure we will
+            // not change this after we check mStopRequested, but before we start the wait.
+            mStopRequested = true;
         }
         mCond.notify_one();
         if (mTimerThread.joinable()) {
             mTimerThread.join();
         }
     }
+
 private:
     mutable std::mutex mLock;
     std::thread mTimerThread;
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehiclePropertyStore.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehiclePropertyStore.h
index 6a02cf3..abbcd35 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehiclePropertyStore.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehiclePropertyStore.h
@@ -70,6 +70,16 @@
      * example wasn't registered. */
     bool writeValue(const VehiclePropValue& propValue, bool updateStatus);
 
+    /*
+     * Stores provided value. Returns true if value was written returns false if config for
+     * example wasn't registered.
+     *
+     * The property value's timestamp will be set to the current ElapsedRealTimeNano.
+     */
+    bool writeValueWithCurrentTimestamp(VehiclePropValue* propValuePtr, bool updateStatus);
+
+    std::unique_ptr<VehiclePropValue> refreshTimestamp(int32_t propId, int32_t areaId);
+
     void removeValue(const VehiclePropValue& propValue);
     void removeValuesForProperty(int32_t propId);
 
@@ -94,6 +104,8 @@
     std::unordered_map<int32_t /* VehicleProperty */, RecordConfig> mConfigs;
 
     PropertyMap mPropertyValues;  // Sorted map of RecordId : VehiclePropValue.
+
+    bool writeValueLocked(const VehiclePropValue& propValue, bool updateStatus);
 };
 
 }  // namespace V2_0
diff --git a/automotive/vehicle/2.0/default/common/src/VehiclePropertyStore.cpp b/automotive/vehicle/2.0/default/common/src/VehiclePropertyStore.cpp
index 6087bfa..c12904e 100644
--- a/automotive/vehicle/2.0/default/common/src/VehiclePropertyStore.cpp
+++ b/automotive/vehicle/2.0/default/common/src/VehiclePropertyStore.cpp
@@ -15,6 +15,7 @@
  */
 #define LOG_TAG "VehiclePropertyStore"
 #include <log/log.h>
+#include <utils/SystemClock.h>
 
 #include <common/include/vhal_v2_0/VehicleUtils.h>
 #include "VehiclePropertyStore.h"
@@ -41,9 +42,7 @@
     mConfigs.insert({ config.prop, RecordConfig { config, tokenFunc } });
 }
 
-bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue,
-                                        bool updateStatus) {
-    MuxGuard g(mLock);
+bool VehiclePropertyStore::writeValueLocked(const VehiclePropValue& propValue, bool updateStatus) {
     if (!mConfigs.count(propValue.prop)) return false;
 
     RecordId recId = getRecordIdLocked(propValue);
@@ -68,6 +67,36 @@
     return true;
 }
 
+bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue, bool updateStatus) {
+    MuxGuard g(mLock);
+
+    return writeValueLocked(propValue, updateStatus);
+}
+
+bool VehiclePropertyStore::writeValueWithCurrentTimestamp(VehiclePropValue* propValuePtr,
+                                                          bool updateStatus) {
+    MuxGuard g(mLock);
+
+    propValuePtr->timestamp = elapsedRealtimeNano();
+    return writeValueLocked(*propValuePtr, updateStatus);
+}
+
+std::unique_ptr<VehiclePropValue> VehiclePropertyStore::refreshTimestamp(int32_t propId,
+                                                                         int32_t areaId) {
+    MuxGuard g(mLock);
+    RecordId recId = getRecordIdLocked(VehiclePropValue{
+            .prop = propId,
+            .areaId = areaId,
+    });
+    auto it = mPropertyValues.find(recId);
+    if (it == mPropertyValues.end()) {
+        return nullptr;
+    }
+
+    it->second.timestamp = elapsedRealtimeNano();
+    return std::make_unique<VehiclePropValue>(it->second);
+}
+
 void VehiclePropertyStore::removeValue(const VehiclePropValue& propValue) {
     MuxGuard g(mLock);
     RecordId recId = getRecordIdLocked(propValue);
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/BackportedPropertyHelper.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/BackportedPropertyHelper.h
new file mode 100644
index 0000000..78ae940
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/BackportedPropertyHelper.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 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 contains backported system property definitions and backported enums.
+
+#pragma once
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+namespace backportedproperty {
+
+/**
+ * Characterization of inputs used for computing location.
+ *
+ * This property must indicate what (if any) data and sensor inputs are considered by the system
+ * when computing the vehicle's location that is shared with Android through the GNSS HAL.
+ *
+ * The value must return a collection of bit flags. The bit flags are defined in
+ * LocationCharacterization. The value must also include exactly one of DEAD_RECKONED or
+ * RAW_GNSS_ONLY among its collection of bit flags.
+ *
+ * When this property is not supported, it is assumed that no additional sensor inputs are fused
+ * into the GNSS updates provided through the GNSS HAL. That is unless otherwise specified
+ * through the GNSS HAL interfaces.
+ *
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
+ */
+constexpr int32_t LOCATION_CHARACTERIZATION = 0x31400C10;
+
+/**
+ * Used by LOCATION_CHARACTERIZATION to enumerate the supported bit flags.
+ *
+ * These flags are used to indicate to what transformations are performed on the
+ * GNSS data before the location data is sent, so that location processing
+ * algorithms can take into account prior fusion.
+ *
+ * This enum can be extended in future releases to include additional bit flags.
+ */
+enum class LocationCharacterization : int32_t {
+    /**
+     * Prior location samples have been used to refine the raw GNSS data (e.g. a
+     * Kalman Filter).
+     */
+    PRIOR_LOCATIONS = 0x1,
+    /**
+     * Gyroscope data has been used to refine the raw GNSS data.
+     */
+    GYROSCOPE_FUSION = 0x2,
+    /**
+     * Accelerometer data has been used to refine the raw GNSS data.
+     */
+    ACCELEROMETER_FUSION = 0x4,
+    /**
+     * Compass data has been used to refine the raw GNSS data.
+     */
+    COMPASS_FUSION = 0x8,
+    /**
+     * Wheel speed has been used to refine the raw GNSS data.
+     */
+    WHEEL_SPEED_FUSION = 0x10,
+    /**
+     * Steering angle has been used to refine the raw GNSS data.
+     */
+    STEERING_ANGLE_FUSION = 0x20,
+    /**
+     * Car speed has been used to refine the raw GNSS data.
+     */
+    CAR_SPEED_FUSION = 0x40,
+    /**
+     * Some effort is made to dead-reckon location. In particular, this means that
+     * relative changes in location have meaning when no GNSS satellite is
+     * available.
+     */
+    DEAD_RECKONED = 0x80,
+    /**
+     * Location is based on GNSS satellite signals without sufficient fusion of
+     * other sensors for complete dead reckoning. This flag should be set when
+     * relative changes to location cannot be relied on when no GNSS satellite is
+     * available.
+     */
+    RAW_GNSS_ONLY = 0x100,
+};
+
+}  // namespace backportedproperty
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index cfa3b0c..4846bfb 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -17,6 +17,7 @@
 #ifndef android_hardware_automotive_vehicle_V2_0_impl_DefaultConfig_H_
 #define android_hardware_automotive_vehicle_V2_0_impl_DefaultConfig_H_
 
+#include "BackportedPropertyHelper.h"
 #include "PropertyUtils.h"
 
 #include <map>
@@ -29,6 +30,9 @@
 
 namespace impl {
 
+using ::android::hardware::automotive::vehicle::V2_0::backportedproperty::LOCATION_CHARACTERIZATION;
+using ::android::hardware::automotive::vehicle::V2_0::backportedproperty::LocationCharacterization;
+
 struct ConfigDeclaration {
     VehiclePropConfig config;
 
@@ -938,7 +942,10 @@
                                   (int)VehicleVendorPermission::PERMISSION_NOT_ACCESSIBLE,
                                   VENDOR_EXTENSION_FLOAT_PROPERTY,
                                   (int)VehicleVendorPermission::PERMISSION_DEFAULT,
-                                  (int)VehicleVendorPermission::PERMISSION_DEFAULT},
+                                  (int)VehicleVendorPermission::PERMISSION_DEFAULT,
+                                  LOCATION_CHARACTERIZATION,
+                                  (int)VehicleVendorPermission::PERMISSION_GET_VENDOR_CATEGORY_INFO,
+                                  (int)VehicleVendorPermission::PERMISSION_NOT_ACCESSIBLE},
                  },
          .initialValue = {.int32Values = {1}}},
 
@@ -1131,6 +1138,15 @@
                 // GsrComplianceRequirementType::GSR_COMPLIANCE_REQUIRED_V1
                 .initialValue = {.int32Values = {1}},
         },
+        {
+                .config =
+                        {
+                                .prop = LOCATION_CHARACTERIZATION,
+                                .access = VehiclePropertyAccess::READ,
+                                .changeMode = VehiclePropertyChangeMode::STATIC,
+                        },
+                .initialValue = {.int32Values = {toInt(LocationCharacterization::RAW_GNSS_ONLY)}},
+        },
 #ifdef ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
         // Vendor propetry for E2E ClusterHomeService testing.
         {
@@ -1195,29 +1211,131 @@
                         },
                 // All supported property IDs. This list is checked by
                 // DefaultConfigSupportedPropertyIds_test.
-                .initialValue =
-                        {.int32Values =
-                                 {291504388, 289472773, 291504390, 289472775, 289407240, 289407241,
-                                  289472780, 286261505, 286261506, 289407235, 289472779, 291504647,
-                                  289408517, 356518832, 356516106, 291504644, 291504649, 291504656,
-                                  291504901, 291504903, 287310600, 291504905, 287310602, 287310603,
-                                  291504908, 291504904, 392168201, 392168202, 289408514, 289408001,
-                                  287310850, 287310851, 287310853, 289408513, 289475088, 289475104,
-                                  289475120, 354419984, 320865540, 320865556, 354419975, 354419976,
-                                  354419986, 354419973, 354419974, 354419978, 354419977, 356517120,
-                                  356517121, 356582673, 356517139, 289408269, 356517131, 358614275,
-                                  291570965, 291505923, 289408270, 289408512, 287310855, 289408000,
-                                  289408008, 289408009, 289407747, 291504900, 568332561, 371198722,
-                                  373295872, 320867268, 322964416, 290521862, 287310858, 287310859,
-                                  289475072, 289475073, 289409539, 299896064, 299896065, 299896066,
-                                  299896067, 289410560, 289410561, 289410562, 289410563, 289410576,
-                                  289410577, 289410578, 289410579, 289476368, 299895808, 639631617,
-                                  627048706, 591397123, 554696964, 289410873, 289410874, 287313669,
-                                  299896583, 299896584, 299896585, 299896586, 299896587, 286265121,
-                                  286265122, 286265123, 290457094, 290459441, 299896626, 290459443,
-                                  289410868, 289476405, 299896630, 289410871, 292556600, 557853201,
-                                  559950353, 555756049, 554707473, 289410887, 557846324, 557911861,
-                                  568332086, 557846327, 560992056, 289476424}},
+                .initialValue = {.int32Values = {291504388,
+                                                 289472773,
+                                                 291504390,
+                                                 289472775,
+                                                 289407240,
+                                                 289407241,
+                                                 289472780,
+                                                 286261505,
+                                                 286261506,
+                                                 289407235,
+                                                 289472779,
+                                                 291504647,
+                                                 289408517,
+                                                 356518832,
+                                                 356516106,
+                                                 291504644,
+                                                 291504649,
+                                                 291504656,
+                                                 291504901,
+                                                 291504903,
+                                                 287310600,
+                                                 291504905,
+                                                 287310602,
+                                                 287310603,
+                                                 291504908,
+                                                 291504904,
+                                                 392168201,
+                                                 392168202,
+                                                 289408514,
+                                                 289408001,
+                                                 287310850,
+                                                 287310851,
+                                                 287310853,
+                                                 289408513,
+                                                 289475088,
+                                                 289475104,
+                                                 289475120,
+                                                 354419984,
+                                                 320865540,
+                                                 320865556,
+                                                 354419975,
+                                                 354419976,
+                                                 354419986,
+                                                 354419973,
+                                                 354419974,
+                                                 354419978,
+                                                 354419977,
+                                                 356517120,
+                                                 356517121,
+                                                 356582673,
+                                                 356517139,
+                                                 289408269,
+                                                 356517131,
+                                                 358614275,
+                                                 291570965,
+                                                 291505923,
+                                                 289408270,
+                                                 289408512,
+                                                 287310855,
+                                                 289408000,
+                                                 289408008,
+                                                 289408009,
+                                                 289407747,
+                                                 291504900,
+                                                 568332561,
+                                                 371198722,
+                                                 373295872,
+                                                 320867268,
+                                                 322964416,
+                                                 290521862,
+                                                 287310858,
+                                                 287310859,
+                                                 289475072,
+                                                 289475073,
+                                                 289409539,
+                                                 299896064,
+                                                 299896065,
+                                                 299896066,
+                                                 299896067,
+                                                 289410560,
+                                                 289410561,
+                                                 289410562,
+                                                 289410563,
+                                                 289410576,
+                                                 289410577,
+                                                 289410578,
+                                                 289410579,
+                                                 289476368,
+                                                 299895808,
+                                                 639631617,
+                                                 627048706,
+                                                 591397123,
+                                                 554696964,
+                                                 289410873,
+                                                 289410874,
+                                                 287313669,
+                                                 299896583,
+                                                 299896584,
+                                                 299896585,
+                                                 299896586,
+                                                 299896587,
+                                                 286265121,
+                                                 286265122,
+                                                 286265123,
+                                                 290457094,
+                                                 290459441,
+                                                 299896626,
+                                                 290459443,
+                                                 289410868,
+                                                 289476405,
+                                                 299896630,
+                                                 289410871,
+                                                 292556600,
+                                                 557853201,
+                                                 559950353,
+                                                 555756049,
+                                                 554707473,
+                                                 289410887,
+                                                 557846324,
+                                                 557911861,
+                                                 568332086,
+                                                 557846327,
+                                                 560992056,
+                                                 289476424,
+                                                 LOCATION_CHARACTERIZATION}},
         },
 #endif  // ENABLE_GET_PROP_CONFIGS_BY_MULTIPLE_REQUESTS
 };
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp
index 318e9dd..b56a190 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp
@@ -57,12 +57,6 @@
     return nullptr;
 }
 
-VehicleHal::VehiclePropValuePtr addTimestamp(VehicleHal::VehiclePropValuePtr v) {
-    if (v.get()) {
-        v->timestamp = elapsedRealtimeNano();
-    }
-    return v;
-}
 }  // namespace
 
 VehicleHal::VehiclePropValuePtr DefaultVehicleHal::createVhalHeartBeatProp() {
@@ -102,7 +96,7 @@
             *outStatus = StatusCode::INTERNAL_ERROR;
         }
     }
-    return addTimestamp(std::move(v));
+    return v;
 }
 
 VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& requestedPropValue,
@@ -118,13 +112,13 @@
     if (propId == OBD2_FREEZE_FRAME) {
         v = getValuePool()->obtainComplex();
         *outStatus = fillObd2FreezeFrame(mPropStore, requestedPropValue, v.get());
-        return addTimestamp(std::move(v));
+        return v;
     }
 
     if (propId == OBD2_FREEZE_FRAME_INFO) {
         v = getValuePool()->obtainComplex();
         *outStatus = fillObd2DtcInfo(mPropStore, v.get());
-        return addTimestamp(std::move(v));
+        return v;
     }
 
     auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
@@ -139,7 +133,7 @@
     } else {
         *outStatus = StatusCode::TRY_AGAIN;
     }
-    return addTimestamp(std::move(v));
+    return v;
 }
 
 std::vector<VehiclePropConfig> DefaultVehicleHal::listProperties() {
@@ -486,26 +480,42 @@
 
 void DefaultVehicleHal::onContinuousPropertyTimer(const std::vector<int32_t>& properties) {
     auto& pool = *getValuePool();
-
     for (int32_t property : properties) {
-        VehiclePropValuePtr v;
+        std::vector<VehiclePropValuePtr> events;
         if (isContinuousProperty(property)) {
-            auto internalPropValue = mPropStore->readValueOrNull(property);
-            if (internalPropValue != nullptr) {
-                v = pool.obtain(*internalPropValue);
+            const VehiclePropConfig* config = mPropStore->getConfigOrNull(property);
+            std::vector<int32_t> areaIds;
+            if (isGlobalProp(property)) {
+                areaIds.push_back(0);
+            } else {
+                for (auto& c : config->areaConfigs) {
+                    areaIds.push_back(c.areaId);
+                }
+            }
+
+            for (int areaId : areaIds) {
+                auto v = pool.obtain(*mPropStore->refreshTimestamp(property, areaId));
+                if (v.get()) {
+                    events.push_back(std::move(v));
+                }
             }
         } else if (property == static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT)) {
             // VHAL_HEARTBEAT is not a continuous value, but it needs to be updated periodically.
             // So, the update is done through onContinuousPropertyTimer.
-            v = doInternalHealthCheck();
+            auto v = doInternalHealthCheck();
+            if (!v.get()) {
+                // Internal health check failed.
+                continue;
+            }
+            mPropStore->writeValueWithCurrentTimestamp(v.get(), /*updateStatus=*/true);
+            events.push_back(std::move(v));
         } else {
             ALOGE("Unexpected onContinuousPropertyTimer for property: 0x%x", property);
             continue;
         }
 
-        if (v.get()) {
-            v->timestamp = elapsedRealtimeNano();
-            doHalEvent(std::move(v));
+        for (VehiclePropValuePtr& event : events) {
+            doHalEvent(std::move(event));
         }
     }
 }
@@ -556,7 +566,7 @@
 void DefaultVehicleHal::onPropertyValue(const VehiclePropValue& value, bool updateStatus) {
     VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value);
 
-    if (mPropStore->writeValue(*updatedPropValue, updateStatus)) {
+    if (mPropStore->writeValueWithCurrentTimestamp(updatedPropValue.get(), updateStatus)) {
         doHalEvent(std::move(updatedPropValue));
     }
 }
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
index edd4484..c876836 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
@@ -81,8 +81,13 @@
 using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME_CLEAR;
 using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME_INFO;
 using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_LIVE_FRAME;
+using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_FRONT_LEFT;
+using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_FRONT_RIGHT;
+using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_REAR_LEFT;
+using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_REAR_RIGHT;
 
 using ::testing::HasSubstr;
+using ::testing::UnorderedElementsAre;
 
 using VehiclePropValuePtr = recyclable_ptr<VehiclePropValue>;
 
@@ -152,7 +157,7 @@
 TEST_F(DefaultVhalImplTest, testListProperties) {
     std::vector<VehiclePropConfig> configs = mHal->listProperties();
 
-    EXPECT_EQ((size_t)124, configs.size());
+    EXPECT_EQ((size_t)125, configs.size());
 }
 
 TEST_F(DefaultVhalImplTest, testGetDefaultPropertyFloat) {
@@ -346,6 +351,38 @@
     EXPECT_EQ(1.0f, lastEvent->value.floatValues[0]);
 }
 
+TEST_F(DefaultVhalImplTest, testSubscribeContinuous_withMultipleAreaIds) {
+    // Clear existing events.
+    mEventQueue.flush();
+    int propId = toInt(VehicleProperty::TIRE_PRESSURE);
+
+    auto status = mHal->subscribe(propId, 1);
+
+    ASSERT_EQ(StatusCode::OK, status);
+
+    std::vector<VehiclePropValuePtr> receivedEvents;
+    // Wait for 2 updates, each for 4 area IDs.
+    waitForEvents(&receivedEvents, 4 * 2);
+
+    std::vector<int> areasForUpdate1;
+    std::vector<int> areasForUpdate2;
+
+    for (size_t i = 0; i < receivedEvents.size(); i++) {
+        ASSERT_EQ(receivedEvents[i]->prop, propId);
+
+        if (i < 4) {
+            areasForUpdate1.push_back(receivedEvents[i]->areaId);
+        } else {
+            areasForUpdate2.push_back(receivedEvents[i]->areaId);
+        }
+    }
+
+    ASSERT_THAT(areasForUpdate1, UnorderedElementsAre(WHEEL_FRONT_LEFT, WHEEL_FRONT_RIGHT,
+                                                      WHEEL_REAR_LEFT, WHEEL_REAR_RIGHT));
+    ASSERT_THAT(areasForUpdate2, UnorderedElementsAre(WHEEL_FRONT_LEFT, WHEEL_FRONT_RIGHT,
+                                                      WHEEL_REAR_LEFT, WHEEL_REAR_RIGHT));
+}
+
 TEST_F(DefaultVhalImplTest, testSubscribeInvalidProp) {
     EXPECT_EQ(StatusCode::INVALID_ARG, mHal->subscribe(toInt(VehicleProperty::INFO_MAKE), 10));
 }
@@ -1318,7 +1355,6 @@
     ASSERT_EQ((size_t)1, events.size());
     ASSERT_EQ((size_t)1, events[0]->value.int32Values.size());
     EXPECT_EQ(2022, events[0]->value.int32Values[0]);
-    EXPECT_EQ(1000, events[0]->timestamp);
 
     VehiclePropValue value;
     StatusCode status;
@@ -1352,7 +1388,6 @@
     ASSERT_EQ((size_t)1, events.size());
     EXPECT_EQ(0, events[0]->value.int32Values[0]);
     EXPECT_EQ(DOOR_1_LEFT, events[0]->areaId);
-    EXPECT_EQ(1000, events[0]->timestamp);
 
     VehiclePropValue value;
     StatusCode status;
@@ -1391,7 +1426,6 @@
     ASSERT_EQ((size_t)1, events.size());
     ASSERT_EQ((size_t)1, events[0]->value.floatValues.size());
     EXPECT_EQ(10.5, events[0]->value.floatValues[0]);
-    EXPECT_EQ(1000, events[0]->timestamp);
 
     VehiclePropValue value;
     StatusCode status;
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
index cc4fae1..acee9b3 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -2078,25 +2078,10 @@
                 enableVariableUpdateRate) {
                 eventMode = VehiclePropertyStore::EventMode::ON_VALUE_CHANGE;
             }
-            auto action = std::make_shared<RecurrentTimer::Callback>([this, propId, areaId,
-                                                                      eventMode] {
-                // Refresh the property value. In real implementation, this should poll the latest
-                // value from vehicle bus. Here, we are just refreshing the existing value with a
-                // new timestamp.
-                auto result = getValue(VehiclePropValue{
-                        .areaId = areaId,
-                        .prop = propId,
-                        .value = {},
-                });
-                if (!result.ok()) {
-                    // Failed to read current value, skip refreshing.
-                    return;
-                }
-
-                mServerSidePropStore->writeValue(std::move(result.value()),
-                                                 /*updateStatus=*/true, eventMode,
-                                                 /*useCurrentTimestamp=*/true);
-            });
+            auto action =
+                    std::make_shared<RecurrentTimer::Callback>([this, propId, areaId, eventMode] {
+                        mServerSidePropStore->refreshTimestamp(propId, areaId, eventMode);
+                    });
             mRecurrentTimer->registerTimerCallback(intervalInNanos, action);
             mRecurrentActions[propIdAreaId] = action;
             return StatusCode::OK;
diff --git a/automotive/vehicle/aidl/impl/grpc/Android.bp b/automotive/vehicle/aidl/impl/grpc/Android.bp
index 06c9600..e5106f8 100644
--- a/automotive/vehicle/aidl/impl/grpc/Android.bp
+++ b/automotive/vehicle/aidl/impl/grpc/Android.bp
@@ -22,9 +22,11 @@
         "aprotoc",
         "protoc-gen-grpc-cpp-plugin",
     ],
-    cmd: "$(location aprotoc) -I$$(dirname $(in)) -Ihardware/interfaces/automotive/vehicle/aidl/impl/proto -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+    cmd: "$(location aprotoc) -I$$(dirname $(location proto/VehicleServer.proto)) -Ihardware/interfaces/automotive/vehicle/aidl/impl/proto -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(location proto/VehicleServer.proto) --grpc_out=$(genDir) --cpp_out=$(genDir)",
     srcs: [
         "proto/VehicleServer.proto",
+        ":libprotobuf-internal-protos",
+        ":VehicleHalProtoFiles",
     ],
     out: [
         "VehicleServer.pb.h",
@@ -39,9 +41,11 @@
         "aprotoc",
         "protoc-gen-grpc-cpp-plugin",
     ],
-    cmd: "$(location aprotoc) -I$$(dirname $(in)) -Ihardware/interfaces/automotive/vehicle/aidl/impl/proto -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+    cmd: "$(location aprotoc) -I$$(dirname $(location proto/VehicleServer.proto)) -Ihardware/interfaces/automotive/vehicle/aidl/impl/proto -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(location proto/VehicleServer.proto) --grpc_out=$(genDir) --cpp_out=$(genDir)",
     srcs: [
         "proto/VehicleServer.proto",
+        ":libprotobuf-internal-protos",
+        ":VehicleHalProtoFiles",
     ],
     out: [
         "VehicleServer.pb.cc",
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/RecurrentTimer.h b/automotive/vehicle/aidl/impl/utils/common/include/RecurrentTimer.h
index cd2b727..712359a 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/RecurrentTimer.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/RecurrentTimer.h
@@ -19,6 +19,8 @@
 
 #include <android-base/thread_annotations.h>
 
+#include <utils/Looper.h>
+#include <atomic>
 #include <memory>
 #include <mutex>
 #include <queue>
@@ -31,6 +33,9 @@
 namespace automotive {
 namespace vehicle {
 
+// Forward declaration
+class RecurrentMessageHandler;
+
 // A thread-safe recurrent timer.
 class RecurrentTimer final {
   public:
@@ -43,49 +48,44 @@
 
     // Registers a recurrent callback for a given interval.
     // Registering the same callback twice will override the interval provided before.
-    void registerTimerCallback(int64_t intervalInNano, std::shared_ptr<Callback> callback);
+    void registerTimerCallback(int64_t intervalInNanos, std::shared_ptr<Callback> callback);
 
     // Unregisters a previously registered recurrent callback.
     void unregisterTimerCallback(std::shared_ptr<Callback> callback);
 
   private:
-    // friend class for unit testing.
+    friend class RecurrentMessageHandler;
+
+    // For unit test
     friend class RecurrentTimerTest;
 
     struct CallbackInfo {
         std::shared_ptr<Callback> callback;
-        int64_t interval;
-        int64_t nextTime;
-        // A flag to indicate whether this CallbackInfo is already outdated and should be ignored.
-        // The reason we need this flag is because we cannot easily remove an element from a heap.
-        bool outdated = false;
-
-        static bool cmp(const std::unique_ptr<CallbackInfo>& lhs,
-                        const std::unique_ptr<CallbackInfo>& rhs);
+        int64_t intervalInNanos;
+        int64_t nextTimeInNanos;
     };
 
+    android::sp<Looper> mLooper;
+    android::sp<RecurrentMessageHandler> mHandler;
+
+    std::atomic<bool> mStopRequested = false;
+    std::atomic<int> mCallbackId = 0;
     std::mutex mLock;
     std::thread mThread;
-    std::condition_variable mCond;
-    bool mStopRequested GUARDED_BY(mLock) = false;
-    // A map to map each callback to its current active CallbackInfo in the mCallbackQueue.
-    std::unordered_map<std::shared_ptr<Callback>, CallbackInfo*> mCallbacks GUARDED_BY(mLock);
-    // A min-heap sorted by nextTime. Note that because we cannot remove arbitrary element from the
-    // heap, a single Callback can have multiple entries in this queue, all but one should be valid.
-    // The rest should be mark as outdated. The valid one is one stored in mCallbacks.
-    std::vector<std::unique_ptr<CallbackInfo>> mCallbackQueue GUARDED_BY(mLock);
+    std::unordered_map<std::shared_ptr<Callback>, int> mIdByCallback GUARDED_BY(mLock);
+    std::unordered_map<int, std::unique_ptr<CallbackInfo>> mCallbackInfoById GUARDED_BY(mLock);
 
-    void loop();
+    void handleMessage(const android::Message& message) EXCLUDES(mLock);
+    int getCallbackIdLocked(std::shared_ptr<Callback> callback) REQUIRES(mLock);
+};
 
-    // Mark the callbackInfo as outdated and should be ignored when popped from the heap.
-    void markOutdatedLocked(CallbackInfo* callback) REQUIRES(mLock);
-    // Remove all outdated callbackInfos from the top of the heap. This function must be called
-    // each time we might introduce outdated elements to the top. We must make sure the heap is
-    // always valid from the top.
-    void removeInvalidCallbackLocked() REQUIRES(mLock);
-    // Gets the next calblack to run (must be valid) from the heap, update its nextTime and put
-    // it back to the heap.
-    std::shared_ptr<Callback> getNextCallbackLocked(int64_t now) REQUIRES(mLock);
+class RecurrentMessageHandler final : public android::MessageHandler {
+  public:
+    RecurrentMessageHandler(RecurrentTimer* timer) { mTimer = timer; }
+    void handleMessage(const android::Message& message) override;
+
+  private:
+    RecurrentTimer* mTimer;
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h b/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h
index 2812c3b..ef36532 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h
@@ -106,6 +106,11 @@
                                 EventMode mode = EventMode::ON_VALUE_CHANGE,
                                 bool useCurrentTimestamp = false) EXCLUDES(mLock);
 
+    // Refresh the timestamp for the stored property value for [propId, areaId]. If eventMode is
+    // always, generates the property update event, otherwise, only update the stored timestamp
+    // without generating event. This operation is atomic with other writeValue operations.
+    void refreshTimestamp(int32_t propId, int32_t areaId, EventMode eventMode) EXCLUDES(mLock);
+
     // Remove a given property value from the property store. The 'propValue' would be used to
     // generate the key for the value to remove.
     void removeValue(
diff --git a/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp b/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp
index c6d3687..8dec695 100644
--- a/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp
@@ -17,6 +17,7 @@
 #include "RecurrentTimer.h"
 
 #include <utils/Log.h>
+#include <utils/Looper.h>
 #include <utils/SystemClock.h>
 
 #include <inttypes.h>
@@ -27,153 +28,119 @@
 namespace automotive {
 namespace vehicle {
 
+namespace {
+
 using ::android::base::ScopedLockAssertion;
 
+constexpr int INVALID_ID = -1;
+
+}  // namespace
+
 RecurrentTimer::RecurrentTimer() {
-    mThread = std::thread(&RecurrentTimer::loop, this);
+    mHandler = sp<RecurrentMessageHandler>::make(this);
+    mLooper = sp<Looper>::make(/*allowNonCallbacks=*/false);
+    mThread = std::thread([this] {
+        Looper::setForThread(mLooper);
+
+        while (!mStopRequested) {
+            mLooper->pollOnce(/*timeoutMillis=*/-1);
+        }
+    });
 }
 
 RecurrentTimer::~RecurrentTimer() {
-    {
-        std::scoped_lock<std::mutex> lockGuard(mLock);
-        mStopRequested = true;
-    }
-    mCond.notify_one();
+    mStopRequested = true;
+    mLooper->removeMessages(mHandler);
+    mLooper->wake();
     if (mThread.joinable()) {
         mThread.join();
     }
 }
 
-void RecurrentTimer::registerTimerCallback(int64_t intervalInNano,
+int RecurrentTimer::getCallbackIdLocked(std::shared_ptr<RecurrentTimer::Callback> callback) {
+    const auto& it = mIdByCallback.find(callback);
+    if (it != mIdByCallback.end()) {
+        return it->second;
+    }
+    return INVALID_ID;
+}
+
+void RecurrentTimer::registerTimerCallback(int64_t intervalInNanos,
                                            std::shared_ptr<RecurrentTimer::Callback> callback) {
     {
         std::scoped_lock<std::mutex> lockGuard(mLock);
 
+        int callbackId = getCallbackIdLocked(callback);
+
+        if (callbackId == INVALID_ID) {
+            callbackId = mCallbackId++;
+            mIdByCallback.insert({callback, callbackId});
+        } else {
+            ALOGI("Replacing an existing timer callback with a new interval, current: %" PRId64
+                  " ns, new: %" PRId64 " ns",
+                  mCallbackInfoById[callbackId]->intervalInNanos, intervalInNanos);
+            mLooper->removeMessages(mHandler, callbackId);
+        }
+
         // Aligns the nextTime to multiply of interval.
-        int64_t nextTime = ceil(uptimeNanos() / intervalInNano) * intervalInNano;
+        int64_t nextTimeInNanos = ceil(uptimeNanos() / intervalInNanos) * intervalInNanos;
 
         std::unique_ptr<CallbackInfo> info = std::make_unique<CallbackInfo>();
         info->callback = callback;
-        info->interval = intervalInNano;
-        info->nextTime = nextTime;
+        info->intervalInNanos = intervalInNanos;
+        info->nextTimeInNanos = nextTimeInNanos;
+        mCallbackInfoById.insert({callbackId, std::move(info)});
 
-        auto it = mCallbacks.find(callback);
-        if (it != mCallbacks.end()) {
-            ALOGI("Replacing an existing timer callback with a new interval, current: %" PRId64
-                  " ns, new: %" PRId64 " ns",
-                  it->second->interval, intervalInNano);
-            markOutdatedLocked(it->second);
-        }
-        mCallbacks[callback] = info.get();
-        mCallbackQueue.push_back(std::move(info));
-        // Insert the last element into the heap.
-        std::push_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
+        mLooper->sendMessageAtTime(nextTimeInNanos, mHandler, Message(callbackId));
     }
-    mCond.notify_one();
 }
 
 void RecurrentTimer::unregisterTimerCallback(std::shared_ptr<RecurrentTimer::Callback> callback) {
     {
         std::scoped_lock<std::mutex> lockGuard(mLock);
 
-        auto it = mCallbacks.find(callback);
-        if (it == mCallbacks.end()) {
+        int callbackId = getCallbackIdLocked(callback);
+
+        if (callbackId == INVALID_ID) {
             ALOGE("No event found to unregister");
             return;
         }
 
-        markOutdatedLocked(it->second);
-        mCallbacks.erase(it);
-    }
-
-    mCond.notify_one();
-}
-
-void RecurrentTimer::markOutdatedLocked(RecurrentTimer::CallbackInfo* info) {
-    info->outdated = true;
-    info->callback = nullptr;
-    // Make sure the first element is always valid.
-    removeInvalidCallbackLocked();
-}
-
-void RecurrentTimer::removeInvalidCallbackLocked() {
-    while (mCallbackQueue.size() != 0 && mCallbackQueue[0]->outdated) {
-        std::pop_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
-        mCallbackQueue.pop_back();
+        mLooper->removeMessages(mHandler, callbackId);
+        mCallbackInfoById.erase(callbackId);
+        mIdByCallback.erase(callback);
     }
 }
 
-std::shared_ptr<RecurrentTimer::Callback> RecurrentTimer::getNextCallbackLocked(int64_t now) {
-    std::pop_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
-    auto& callbackInfo = mCallbackQueue[mCallbackQueue.size() - 1];
-    auto nextCallback = callbackInfo->callback;
-    // intervalCount is the number of interval we have to advance until we pass now.
-    size_t intervalCount = (now - callbackInfo->nextTime) / callbackInfo->interval + 1;
-    callbackInfo->nextTime += intervalCount * callbackInfo->interval;
-    std::push_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
+void RecurrentTimer::handleMessage(const Message& message) {
+    std::shared_ptr<RecurrentTimer::Callback> callback;
+    {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
 
-    // Make sure the first element is always valid.
-    removeInvalidCallbackLocked();
+        int callbackId = message.what;
 
-    return nextCallback;
-}
-
-void RecurrentTimer::loop() {
-    std::vector<std::shared_ptr<Callback>> callbacksToRun;
-    while (true) {
-        {
-            std::unique_lock<std::mutex> uniqueLock(mLock);
-            ScopedLockAssertion lockAssertion(mLock);
-            // Wait until the timer exits or we have at least one recurrent callback.
-            mCond.wait(uniqueLock, [this] {
-                ScopedLockAssertion lockAssertion(mLock);
-                return mStopRequested || mCallbackQueue.size() != 0;
-            });
-
-            int64_t interval;
-            if (mStopRequested) {
-                return;
-            }
-            // The first element is the nearest next event.
-            int64_t nextTime = mCallbackQueue[0]->nextTime;
-            int64_t now = uptimeNanos();
-
-            if (nextTime > now) {
-                interval = nextTime - now;
-            } else {
-                interval = 0;
-            }
-
-            // Wait for the next event or the timer exits.
-            if (mCond.wait_for(uniqueLock, std::chrono::nanoseconds(interval), [this] {
-                    ScopedLockAssertion lockAssertion(mLock);
-                    return mStopRequested;
-                })) {
-                return;
-            }
-
-            now = uptimeNanos();
-            callbacksToRun.clear();
-            while (mCallbackQueue.size() > 0) {
-                int64_t nextTime = mCallbackQueue[0]->nextTime;
-                if (nextTime > now) {
-                    break;
-                }
-
-                callbacksToRun.push_back(getNextCallbackLocked(now));
-            }
+        auto it = mCallbackInfoById.find(callbackId);
+        if (it == mCallbackInfoById.end()) {
+            ALOGW("The event for callback ID: %d is outdated, ignore", callbackId);
+            return;
         }
 
-        // Do not execute the callback while holding the lock.
-        for (size_t i = 0; i < callbacksToRun.size(); i++) {
-            (*callbacksToRun[i])();
-        }
+        CallbackInfo* callbackInfo = it->second.get();
+        callback = callbackInfo->callback;
+        int64_t nowNanos = uptimeNanos();
+        // intervalCount is the number of interval we have to advance until we pass now.
+        size_t intervalCount =
+                (nowNanos - callbackInfo->nextTimeInNanos) / callbackInfo->intervalInNanos + 1;
+        callbackInfo->nextTimeInNanos += intervalCount * callbackInfo->intervalInNanos;
+
+        mLooper->sendMessageAtTime(callbackInfo->nextTimeInNanos, mHandler, Message(callbackId));
     }
+
+    (*callback)();
 }
 
-bool RecurrentTimer::CallbackInfo::cmp(const std::unique_ptr<RecurrentTimer::CallbackInfo>& lhs,
-                                       const std::unique_ptr<RecurrentTimer::CallbackInfo>& rhs) {
-    return lhs->nextTime > rhs->nextTime;
+void RecurrentMessageHandler::handleMessage(const Message& message) {
+    mTimer->handleMessage(message);
 }
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp b/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
index b879850..7d9d8b7 100644
--- a/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
@@ -176,6 +176,42 @@
     return {};
 }
 
+void VehiclePropertyStore::refreshTimestamp(int32_t propId, int32_t areaId, EventMode eventMode) {
+    VehiclePropValue updatedValue;
+    OnValueChangeCallback onValueChangeCallback = nullptr;
+    {
+        std::scoped_lock<std::mutex> g(mLock);
+
+        VehiclePropertyStore::Record* record = getRecordLocked(propId);
+        if (record == nullptr) {
+            return;
+        }
+
+        VehiclePropValue propValue = {
+                .areaId = areaId,
+                .prop = propId,
+                .value = {},
+        };
+
+        VehiclePropertyStore::RecordId recId = getRecordIdLocked(propValue, *record);
+        if (auto it = record->values.find(recId); it != record->values.end()) {
+            it->second->timestamp = elapsedRealtimeNano();
+            updatedValue = *(it->second);
+        } else {
+            return;
+        }
+        if (!mOnValueChangeCallback) {
+            return;
+        }
+        onValueChangeCallback = mOnValueChangeCallback;
+    }
+
+    // Invoke the callback outside the lock to prevent dead-lock.
+    if (eventMode == EventMode::ALWAYS) {
+        onValueChangeCallback(updatedValue);
+    }
+}
+
 void VehiclePropertyStore::removeValue(const VehiclePropValue& propValue) {
     std::scoped_lock<std::mutex> g(mLock);
 
diff --git a/automotive/vehicle/aidl/impl/utils/common/test/RecurrentTimerTest.cpp b/automotive/vehicle/aidl/impl/utils/common/test/RecurrentTimerTest.cpp
index 62046f3..e8ac2a5 100644
--- a/automotive/vehicle/aidl/impl/utils/common/test/RecurrentTimerTest.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/test/RecurrentTimerTest.cpp
@@ -60,9 +60,14 @@
         mCallbacks.clear();
     }
 
-    size_t countTimerCallbackQueue(RecurrentTimer* timer) {
+    size_t countCallbackInfoById(RecurrentTimer* timer) {
         std::scoped_lock<std::mutex> lockGuard(timer->mLock);
-        return timer->mCallbackQueue.size();
+        return timer->mCallbackInfoById.size();
+    }
+
+    size_t countIdByCallback(RecurrentTimer* timer) {
+        std::scoped_lock<std::mutex> lockGuard(timer->mLock);
+        return timer->mIdByCallback.size();
     }
 
   private:
@@ -109,6 +114,9 @@
             << "Not enough callbacks called before timeout";
 
     timer.unregisterTimerCallback(action);
+
+    ASSERT_EQ(countCallbackInfoById(&timer), 0u);
+    ASSERT_EQ(countIdByCallback(&timer), 0u);
 }
 
 TEST_F(RecurrentTimerTest, testDestroyTimerWithCallback) {
@@ -198,8 +206,8 @@
 
     timer.unregisterTimerCallback(action);
 
-    // Make sure there is no item in the callback queue.
-    ASSERT_EQ(countTimerCallbackQueue(&timer), static_cast<size_t>(0));
+    ASSERT_EQ(countCallbackInfoById(&timer), 0u);
+    ASSERT_EQ(countIdByCallback(&timer), 0u);
 }
 
 TEST_F(RecurrentTimerTest, testRegisterCallbackMultipleTimesNoDeadLock) {
diff --git a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
index 63964ef..a63cb84 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
@@ -59,6 +59,7 @@
 using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
 using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
+using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWindow;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs;
diff --git a/automotive/vehicle/tools/translate_aidl_enums.py b/automotive/vehicle/tools/translate_aidl_enums.py
new file mode 100644
index 0000000..d224f6f
--- /dev/null
+++ b/automotive/vehicle/tools/translate_aidl_enums.py
@@ -0,0 +1,231 @@
+#!/usr/bin/python3
+
+# Copyright (C) 2023 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.
+#
+"""A script to generate ENUM_NAME.java file and test files using ENUM_NAME.aidl file.
+
+   Need ANDROID_BUILD_TOP environmental variable to be set. This script will update ENUM_NAME.java
+   under packages/services/Car/car-lib/src/android/car/hardware/property, as well as the
+   ENUM_NAMETest.java files in cts/tests/tests/car/src/android/car/cts and
+   packages/services/Car/tests/android_car_api_test/src/android/car/apitest
+
+   Usage:
+   $ python translate_aidl_enums.py ENUM_NAME.aidl
+"""
+import os
+import sys
+
+LICENSE = """/*
+ * Copyright (C) 2023 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.
+ */
+"""
+
+class EnumParser:
+    def __init__(self, file_path, file_name):
+        self.filePath = file_path
+        self.fileName = file_name
+        self.lowerFileName = self.fileName[0].lower() + self.fileName[1:]
+        self.enums = []
+        self.outputMsg = []
+        self.outputMsg.append(LICENSE)
+        self.outputMsg.append("\npackage android.car.hardware.property;\n")
+        self.outputMsg.append("""
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import com.android.car.internal.util.ConstantDebugUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+""")
+
+        with open(self.filePath, 'r') as f:
+            for line in f.readlines()[16:]:
+                if line in ["package android.hardware.automotive.vehicle;\n",
+                            "@VintfStability\n",
+                            '@Backing(type="int")\n']:
+                    continue
+
+                msg = line
+                msgSplit = msg.strip().split()
+                if len(msgSplit) > 0 and msgSplit[0] == "enum":
+                    msgSplit[0] = "public final class"
+                    msg = " ".join(msgSplit) + "\n"
+                elif len(msgSplit) > 1 and msgSplit[1] == '=':
+                    msgSplit.insert(0, "    public static final int")
+                    self.enums.append(msgSplit[1])
+                    msgSplit[-1] = msgSplit[-1][:-1] + ";\n"
+                    msg = " ".join(msgSplit)
+                elif msg == "}\n":
+                    self.outputMsg.append("""
+    private {2}() {{}}
+
+    /**
+     * Returns a user-friendly representation of {{@code {2}}}.
+     */
+    @NonNull
+    public static String toString(@{2}Int int {0}) {{
+        String {0}String = ConstantDebugUtils.toName(
+                {2}.class, {0});
+        return ({0}String != null)
+                ? {0}String
+                : "0x" + Integer.toHexString({0});
+    }}
+
+    /** @hide */
+    @IntDef({1})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface {2}Int {{}}\n""".format(self.lowerFileName, "{" + ", ".join(self.enums) + "}",
+                                              self.fileName))
+                self.outputMsg.append(msg)
+        self.outputMsg.append("TODO: delete this line and manually update this file with app-facing documentation and necessary tags.\n")
+
+        self.outputMsgApiTest = []
+        self.outputMsgApiTest.append(LICENSE)
+        self.outputMsgApiTest.append("""package android.car.apitest;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@SmallTest
+@RunWith(Parameterized.class)
+public class {0}Test {{
+    private final int mJavaConstantValue;
+    private final int mHalConstantValue;
+
+    public {0}Test(int javaConstantValue, int halConstantValue) {{
+        mJavaConstantValue = javaConstantValue;
+        mHalConstantValue = halConstantValue;
+    }}
+
+    @Parameterized.Parameters
+    public static Collection constantValues() {{
+        return Arrays.asList(
+                new Object[][] {{""".format(self.fileName))
+        for enum in self.enums:
+            self.outputMsgApiTest.append("""
+                        {{
+                                android.car.hardware.property.{0}.{1},
+                                android.hardware.automotive.vehicle.{0}.{1}
+                        }},""".format(self.fileName, enum))
+        self.outputMsgApiTest.append("""
+                });
+    }
+
+    @Test
+    public void testMatchWithVehicleHal() {
+        assertWithMessage("Java constant")
+                .that(mJavaConstantValue)
+                .isEqualTo(mHalConstantValue);
+    }
+}
+""")
+
+        self.outputMsgCtsTest = []
+        self.outputMsgCtsTest.append(LICENSE)
+        self.outputMsgCtsTest.append("""
+package android.car.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.car.cts.utils.VehiclePropertyUtils;
+import android.car.hardware.property.{0};
+
+import org.junit.Test;
+
+import java.util.List;
+
+public class {0}Test {{
+
+    @Test
+    public void testToString() {{""".format(self.fileName))
+        for enum in self.enums:
+            self.outputMsgCtsTest.append("""
+        assertThat({0}.toString(
+                {0}.{1}))
+                .isEqualTo("{1}");""".format(self.fileName, enum))
+        self.outputMsgCtsTest.append("""
+        assertThat({0}.toString({1})).isEqualTo("{2}");
+        assertThat({0}.toString(12)).isEqualTo("0xc");
+    }}
+
+    @Test
+    public void testAll{0}sAreMappedInToString() {{
+        List<Integer> {3}s =
+                VehiclePropertyUtils.getIntegersFromDataEnums({0}.class);
+        for (Integer {3} : {3}s) {{
+            String {3}String = {0}.toString(
+                    {3});
+            assertWithMessage("%s starts with 0x", {3}String).that(
+                    {3}String.startsWith("0x")).isFalse();
+        }}
+    }}
+}}
+""".format(self.fileName, len(self.enums), hex(len(self.enums)), self.lowerFileName))
+
+def main():
+    if len(sys.argv) != 2:
+        print("Usage: {} enum_aidl_file".format(sys.argv[0]))
+        sys.exit(1)
+    print("WARNING: This file only generates the base enum values in the framework layer. The "
+          + "generated files must be reviewed by you and edited if any additional changes are "
+          + "required. The java enum file should be updated with app-developer facing "
+          + "documentation, the @FlaggedApi tag for the new API, and with the @SystemApi tag if "
+          + "the new property is system API")
+    file_path = sys.argv[1]
+    file_name = file_path.split('/')[-1][:-5]
+    parser = EnumParser(file_path, file_name)
+
+    android_top = os.environ['ANDROID_BUILD_TOP']
+    if not android_top:
+        print('ANDROID_BUILD_TOP is not in environmental variable, please run source and lunch '
+              + 'at the android root')
+
+    with open(android_top + "/packages/services/Car/car-lib/src/android/car/hardware/property/"
+              + file_name + ".java", 'w') as f:
+        f.write("".join(parser.outputMsg))
+
+    with open(android_top
+              + "/packages/services/Car/tests/android_car_api_test/src/android/car/apitest/"
+              + file_name + "Test.java", 'w') as f:
+        f.write("".join(parser.outputMsgApiTest))
+
+    with open(android_top + "/cts/tests/tests/car/src/android/car/cts/" + file_name + "Test.java",
+              'w') as f:
+        f.write("".join(parser.outputMsgCtsTest))
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/biometrics/common/aidl/Android.bp b/biometrics/common/aidl/Android.bp
index b41a937..8502a82 100644
--- a/biometrics/common/aidl/Android.bp
+++ b/biometrics/common/aidl/Android.bp
@@ -13,7 +13,7 @@
     srcs: [
         "android/hardware/biometrics/common/*.aidl",
     ],
-    frozen: true,
+    frozen: false,
     stability: "vintf",
     backend: {
         java: {
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AacObjectType.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AacObjectType.aidl
index 2148244..418dd7a 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AacObjectType.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AacObjectType.aidl
@@ -34,8 +34,8 @@
 package android.hardware.bluetooth.audio;
 @Backing(type="byte") @VintfStability
 enum AacObjectType {
-  MPEG2_LC = 0,
-  MPEG4_LC = 1,
-  MPEG4_LTP = 2,
-  MPEG4_SCALABLE = 3,
+  MPEG2_LC,
+  MPEG4_LC,
+  MPEG4_LTP,
+  MPEG4_SCALABLE,
 }
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AptxAdaptiveChannelMode.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AptxAdaptiveChannelMode.aidl
index 0499b70..675f9f2 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AptxAdaptiveChannelMode.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AptxAdaptiveChannelMode.aidl
@@ -38,5 +38,5 @@
   MONO = 1,
   DUAL_MONO = 2,
   TWS_STEREO = 4,
-  UNKNOWN = 255,
+  UNKNOWN = 0xFF,
 }
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AptxAdaptiveInputMode.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AptxAdaptiveInputMode.aidl
index f702939..a18303e 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AptxAdaptiveInputMode.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AptxAdaptiveInputMode.aidl
@@ -34,6 +34,6 @@
 package android.hardware.bluetooth.audio;
 @Backing(type="int") @VintfStability
 enum AptxAdaptiveInputMode {
-  STEREO = 0,
-  DUAL_MONO = 1,
+  STEREO = 0x00,
+  DUAL_MONO = 0x01,
 }
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AptxMode.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AptxMode.aidl
index d5dd9d9..dd8cf08 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AptxMode.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AptxMode.aidl
@@ -34,8 +34,8 @@
 package android.hardware.bluetooth.audio;
 @Backing(type="int") @VintfStability
 enum AptxMode {
-  UNKNOWN = 0,
-  HIGH_QUALITY = 4096,
-  LOW_LATENCY = 8192,
-  ULTRA_LOW_LATENCY = 16384,
+  UNKNOWN = 0x00,
+  HIGH_QUALITY = 0x1000,
+  LOW_LATENCY = 0x2000,
+  ULTRA_LOW_LATENCY = 0x4000,
 }
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AudioLocation.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AudioLocation.aidl
index 319a5e2..941344c 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AudioLocation.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AudioLocation.aidl
@@ -35,6 +35,6 @@
 @Backing(type="int") @VintfStability
 enum AudioLocation {
   UNKNOWN = 1,
-  FRONT_LEFT = 2,
-  FRONT_RIGHT = 4,
+  FRONT_LEFT = (1 << 1) /* 2 */,
+  FRONT_RIGHT = (1 << 2) /* 4 */,
 }
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/ChannelMode.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/ChannelMode.aidl
index feacb80..2bb5cd8 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/ChannelMode.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/ChannelMode.aidl
@@ -34,8 +34,8 @@
 package android.hardware.bluetooth.audio;
 @Backing(type="byte") @VintfStability
 enum ChannelMode {
-  UNKNOWN = 0,
-  MONO = 1,
-  STEREO = 2,
-  DUALMONO = 3,
+  UNKNOWN,
+  MONO,
+  STEREO,
+  DUALMONO,
 }
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecType.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecType.aidl
index 3e204f9..d4f205e 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecType.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecType.aidl
@@ -34,16 +34,16 @@
 package android.hardware.bluetooth.audio;
 @Backing(type="int") @VintfStability
 enum CodecType {
-  UNKNOWN = 0,
-  SBC = 1,
-  AAC = 2,
-  APTX = 3,
-  APTX_HD = 4,
-  LDAC = 5,
-  LC3 = 6,
-  VENDOR = 7,
-  APTX_ADAPTIVE = 8,
-  OPUS = 9,
-  APTX_ADAPTIVE_LE = 10,
-  APTX_ADAPTIVE_LEX = 11,
+  UNKNOWN,
+  SBC,
+  AAC,
+  APTX,
+  APTX_HD,
+  LDAC,
+  LC3,
+  VENDOR,
+  APTX_ADAPTIVE,
+  OPUS,
+  APTX_ADAPTIVE_LE,
+  APTX_ADAPTIVE_LEX,
 }
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/LdacChannelMode.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/LdacChannelMode.aidl
index 88d6faf..3d80c4b 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/LdacChannelMode.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/LdacChannelMode.aidl
@@ -34,8 +34,8 @@
 package android.hardware.bluetooth.audio;
 @Backing(type="byte") @VintfStability
 enum LdacChannelMode {
-  UNKNOWN = 0,
-  STEREO = 1,
-  DUAL = 2,
-  MONO = 3,
+  UNKNOWN,
+  STEREO,
+  DUAL,
+  MONO,
 }
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/LdacQualityIndex.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/LdacQualityIndex.aidl
index 35e4358..a332dc5 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/LdacQualityIndex.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/LdacQualityIndex.aidl
@@ -34,8 +34,8 @@
 package android.hardware.bluetooth.audio;
 @Backing(type="byte") @VintfStability
 enum LdacQualityIndex {
-  HIGH = 0,
-  MID = 1,
-  LOW = 2,
-  ABR = 3,
+  HIGH,
+  MID,
+  LOW,
+  ABR,
 }
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/SbcAllocMethod.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/SbcAllocMethod.aidl
index 091f6d7..9cf65d5 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/SbcAllocMethod.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/SbcAllocMethod.aidl
@@ -34,6 +34,6 @@
 package android.hardware.bluetooth.audio;
 @Backing(type="byte") @VintfStability
 enum SbcAllocMethod {
-  ALLOC_MD_S = 0,
-  ALLOC_MD_L = 1,
+  ALLOC_MD_S,
+  ALLOC_MD_L,
 }
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/SbcChannelMode.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/SbcChannelMode.aidl
index 6441a99..7779aa0 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/SbcChannelMode.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/SbcChannelMode.aidl
@@ -34,9 +34,9 @@
 package android.hardware.bluetooth.audio;
 @Backing(type="byte") @VintfStability
 enum SbcChannelMode {
-  UNKNOWN = 0,
-  JOINT_STEREO = 1,
-  STEREO = 2,
-  DUAL = 3,
-  MONO = 4,
+  UNKNOWN,
+  JOINT_STEREO,
+  STEREO,
+  DUAL,
+  MONO,
 }
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/SessionType.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/SessionType.aidl
index 33a3187..4b2c10f 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/SessionType.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/SessionType.aidl
@@ -34,16 +34,16 @@
 package android.hardware.bluetooth.audio;
 @Backing(type="byte") @VintfStability
 enum SessionType {
-  UNKNOWN = 0,
-  A2DP_SOFTWARE_ENCODING_DATAPATH = 1,
-  A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH = 2,
-  HEARING_AID_SOFTWARE_ENCODING_DATAPATH = 3,
-  LE_AUDIO_SOFTWARE_ENCODING_DATAPATH = 4,
-  LE_AUDIO_SOFTWARE_DECODING_DATAPATH = 5,
-  LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH = 6,
-  LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH = 7,
-  LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH = 8,
-  LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH = 9,
-  A2DP_SOFTWARE_DECODING_DATAPATH = 10,
-  A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH = 11,
+  UNKNOWN,
+  A2DP_SOFTWARE_ENCODING_DATAPATH,
+  A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH,
+  HEARING_AID_SOFTWARE_ENCODING_DATAPATH,
+  LE_AUDIO_SOFTWARE_ENCODING_DATAPATH,
+  LE_AUDIO_SOFTWARE_DECODING_DATAPATH,
+  LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH,
+  LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH,
+  LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH,
+  LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH,
+  A2DP_SOFTWARE_DECODING_DATAPATH,
+  A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH,
 }
diff --git a/bluetooth/finder/aidl/Android.bp b/bluetooth/finder/aidl/Android.bp
new file mode 100644
index 0000000..e606d2d
--- /dev/null
+++ b/bluetooth/finder/aidl/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aidl_interface {
+    name: "android.hardware.bluetooth.finder",
+    vendor_available: true,
+    srcs: ["android/hardware/bluetooth/finder/*.aidl"],
+    stability: "vintf",
+
+    backend: {
+        ndk: {
+            enabled: true,
+        },
+        java: {
+            enabled: true,
+            platform_apis: true,
+        },
+    },
+}
diff --git a/bluetooth/finder/aidl/aidl_api/android.hardware.bluetooth.finder/current/android/hardware/bluetooth/finder/Eid.aidl b/bluetooth/finder/aidl/aidl_api/android.hardware.bluetooth.finder/current/android/hardware/bluetooth/finder/Eid.aidl
new file mode 100644
index 0000000..42461c5
--- /dev/null
+++ b/bluetooth/finder/aidl/aidl_api/android.hardware.bluetooth.finder/current/android/hardware/bluetooth/finder/Eid.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 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.bluetooth.finder;
+@VintfStability
+parcelable Eid {
+  byte[20] bytes;
+}
diff --git a/bluetooth/finder/aidl/aidl_api/android.hardware.bluetooth.finder/current/android/hardware/bluetooth/finder/IBluetoothFinder.aidl b/bluetooth/finder/aidl/aidl_api/android.hardware.bluetooth.finder/current/android/hardware/bluetooth/finder/IBluetoothFinder.aidl
new file mode 100644
index 0000000..4bc9041
--- /dev/null
+++ b/bluetooth/finder/aidl/aidl_api/android.hardware.bluetooth.finder/current/android/hardware/bluetooth/finder/IBluetoothFinder.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 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.bluetooth.finder;
+@VintfStability
+interface IBluetoothFinder {
+  void sendEids(in android.hardware.bluetooth.finder.Eid[] eids);
+  void setPoweredOffFinderMode(in boolean enable);
+  boolean getPoweredOffFinderMode();
+}
diff --git a/bluetooth/finder/aidl/android/hardware/bluetooth/finder/Eid.aidl b/bluetooth/finder/aidl/android/hardware/bluetooth/finder/Eid.aidl
new file mode 100644
index 0000000..ae9b159
--- /dev/null
+++ b/bluetooth/finder/aidl/android/hardware/bluetooth/finder/Eid.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 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.bluetooth.finder;
+
+/**
+ * Ephemeral Identifier
+ */
+@VintfStability
+parcelable Eid {
+    byte[20] bytes;
+}
diff --git a/bluetooth/finder/aidl/android/hardware/bluetooth/finder/IBluetoothFinder.aidl b/bluetooth/finder/aidl/android/hardware/bluetooth/finder/IBluetoothFinder.aidl
new file mode 100644
index 0000000..615739b
--- /dev/null
+++ b/bluetooth/finder/aidl/android/hardware/bluetooth/finder/IBluetoothFinder.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.bluetooth.finder;
+
+import android.hardware.bluetooth.finder.Eid;
+
+@VintfStability
+interface IBluetoothFinder {
+    /**
+     * API to set the EIDs to the Bluetooth Controller
+     *
+     * @param eids array of 20 bytes EID to the Bluetooth
+     * controller
+     */
+    void sendEids(in Eid[] eids);
+
+    /**
+     * API to enable the powered-off finder feature, which allows the Bluetooth controller to send
+     * beacons after the device is powered off.
+     *
+     * @param enable true to enable; false to disable
+     */
+    void setPoweredOffFinderMode(in boolean enable);
+
+    /**
+     * API for retrieving feature enablement status
+     *
+     * @return the value last set by setPoweredOffFinderMode, false if setPoweredOffFinderMode was
+     *         never been invoked since boot.
+     */
+    boolean getPoweredOffFinderMode();
+}
diff --git a/bluetooth/finder/aidl/default/Android.bp b/bluetooth/finder/aidl/default/Android.bp
new file mode 100644
index 0000000..b364ae1
--- /dev/null
+++ b/bluetooth/finder/aidl/default/Android.bp
@@ -0,0 +1,42 @@
+// Copyright (C) 2023 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+    name: "android.hardware.bluetooth.finder-service.default",
+    relative_install_path: "hw",
+    init_rc: ["bluetooth-finder-service-default.rc"],
+    vintf_fragments: [":manifest_android.hardware.bluetooth.finder-service.default.xml"],
+    vendor: true,
+    srcs: [
+        "BluetoothFinder.cpp",
+        "service.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.bluetooth.finder-V1-ndk",
+        "libbase",
+        "libbinder_ndk",
+        "libhidlbase",
+        "libutils",
+        "liblog",
+    ],
+}
+
+filegroup {
+    name: "manifest_android.hardware.bluetooth.finder-service.default.xml",
+    srcs: ["bluetooth-finder-service-default.xml"],
+}
diff --git a/bluetooth/finder/aidl/default/BluetoothFinder.cpp b/bluetooth/finder/aidl/default/BluetoothFinder.cpp
new file mode 100644
index 0000000..236a1f8
--- /dev/null
+++ b/bluetooth/finder/aidl/default/BluetoothFinder.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BluetoothFinder.h"
+
+namespace aidl::android::hardware::bluetooth::finder::impl {
+
+::ndk::ScopedAStatus BluetoothFinder::sendEids(const ::std::vector<Eid>& keys) {
+  keys_.insert(keys_.end(), keys.begin(), keys.end());
+  return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus BluetoothFinder::setPoweredOffFinderMode(bool enable) {
+  pof_enabled_ = enable;
+  return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus BluetoothFinder::getPoweredOffFinderMode(
+    bool* _aidl_return) {
+  *_aidl_return = pof_enabled_;
+  return ::ndk::ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::bluetooth::finder::impl
diff --git a/bluetooth/finder/aidl/default/BluetoothFinder.h b/bluetooth/finder/aidl/default/BluetoothFinder.h
new file mode 100644
index 0000000..16110f6
--- /dev/null
+++ b/bluetooth/finder/aidl/default/BluetoothFinder.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/android/hardware/bluetooth/finder/BnBluetoothFinder.h>
+
+#include <vector>
+
+namespace aidl::android::hardware::bluetooth::finder::impl {
+
+using ::aidl::android::hardware::bluetooth::finder::BnBluetoothFinder;
+using ::aidl::android::hardware::bluetooth::finder::Eid;
+
+class BluetoothFinder : public BnBluetoothFinder {
+ public:
+  BluetoothFinder() = default;
+
+  ::ndk::ScopedAStatus sendEids(const ::std::vector<Eid>& keys) override;
+  ::ndk::ScopedAStatus setPoweredOffFinderMode(bool enable) override;
+  ::ndk::ScopedAStatus getPoweredOffFinderMode(bool* _aidl_return) override;
+
+ private:
+  bool pof_enabled_;
+  std::vector<Eid> keys_;
+};
+
+}  // namespace aidl::android::hardware::bluetooth::finder::impl
diff --git a/bluetooth/finder/aidl/default/bluetooth-finder-service-default.rc b/bluetooth/finder/aidl/default/bluetooth-finder-service-default.rc
new file mode 100644
index 0000000..fea07f0
--- /dev/null
+++ b/bluetooth/finder/aidl/default/bluetooth-finder-service-default.rc
@@ -0,0 +1,6 @@
+service vendor.bluetooth.finder-default /vendor/bin/hw/android.hardware.bluetooth.finder.default
+    class hal
+    capabilities BLOCK_SUSPEND NET_ADMIN SYS_NICE
+    user bluetooth
+    group bluetooth
+    task_profiles ServicePerformance
diff --git a/bluetooth/finder/aidl/default/bluetooth-finder-service-default.xml b/bluetooth/finder/aidl/default/bluetooth-finder-service-default.xml
new file mode 100644
index 0000000..be7c00d
--- /dev/null
+++ b/bluetooth/finder/aidl/default/bluetooth-finder-service-default.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.bluetooth.finder</name>
+        <version>1</version>
+        <fqname>IBluetoothFinder/default</fqname>
+    </hal>
+</manifest>
diff --git a/bluetooth/finder/aidl/default/service.cpp b/bluetooth/finder/aidl/default/service.cpp
new file mode 100644
index 0000000..a117df8
--- /dev/null
+++ b/bluetooth/finder/aidl/default/service.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "aidl.android.hardware.bluetooth.finder.default"
+
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <utils/Log.h>
+
+#include "BluetoothFinder.h"
+
+using ::aidl::android::hardware::bluetooth::finder::impl::BluetoothFinder;
+
+int main(int /* argc */, char** /* argv */) {
+  ALOGI("Bluetooth Finder HAL registering");
+  if (!ABinderProcess_setThreadPoolMaxThreadCount(0)) {
+    ALOGE("failed to set thread pool max thread count");
+    return 1;
+  }
+
+  std::shared_ptr<BluetoothFinder> service =
+      ndk::SharedRefBase::make<BluetoothFinder>();
+  std::string instance =
+      std::string() + BluetoothFinder::descriptor + "/default";
+  auto result =
+      AServiceManager_addService(service->asBinder().get(), instance.c_str());
+  if (result == STATUS_OK) {
+    ABinderProcess_joinThreadPool();
+  } else {
+    ALOGE("Could not register as a service!");
+  }
+  return 0;
+}
diff --git a/bluetooth/finder/aidl/vts/Android.bp b/bluetooth/finder/aidl/vts/Android.bp
new file mode 100644
index 0000000..6b0285e
--- /dev/null
+++ b/bluetooth/finder/aidl/vts/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2023 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 {
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalBluetoothFinderTargetTest",
+    defaults: [
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: ["VtsHalBluetoothFinderTargetTest.cpp"],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libbinder_ndk",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: [
+        "android.hardware.bluetooth.finder-V1-ndk",
+        "libbluetooth-types",
+    ],
+    test_config: "VtsHalBluetoothFinderTargetTest.xml",
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/bluetooth/finder/aidl/vts/VtsHalBluetoothFinderTargetTest.cpp b/bluetooth/finder/aidl/vts/VtsHalBluetoothFinderTargetTest.cpp
new file mode 100644
index 0000000..be07a7d
--- /dev/null
+++ b/bluetooth/finder/aidl/vts/VtsHalBluetoothFinderTargetTest.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/bluetooth/finder/IBluetoothFinder.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+
+#include <array>
+#include <vector>
+
+using ::aidl::android::hardware::bluetooth::finder::Eid;
+using ::aidl::android::hardware::bluetooth::finder::IBluetoothFinder;
+using ::ndk::ScopedAStatus;
+
+class BluetoothFinderTest : public ::testing::TestWithParam<std::string> {
+ public:
+  virtual void SetUp() override {
+    ALOGI("SetUp Finder Test");
+    bluetooth_finder = IBluetoothFinder::fromBinder(
+        ndk::SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
+    ASSERT_NE(bluetooth_finder, nullptr);
+  }
+
+  virtual void TearDown() override {
+    ALOGI("TearDown Finder Test");
+    bluetooth_finder = nullptr;
+    ASSERT_EQ(bluetooth_finder, nullptr);
+  }
+
+  ScopedAStatus sendEids(uint8_t num);
+  ScopedAStatus setPoweredOffFinderMode(bool enable);
+  ScopedAStatus getPoweredOffFinderMode(bool* status);
+
+ private:
+  std::shared_ptr<IBluetoothFinder> bluetooth_finder;
+};
+
+ScopedAStatus BluetoothFinderTest::sendEids(uint8_t numKeys) {
+  std::vector<Eid> keys(numKeys);
+  for (uint_t i = 0; i < numKeys; i++) {
+    std::array<uint8_t, 20> key;
+    key.fill(i + 1);
+    keys[i].bytes = key;
+  }
+  return bluetooth_finder->sendEids(keys);
+}
+
+ScopedAStatus BluetoothFinderTest::setPoweredOffFinderMode(bool enable) {
+  return bluetooth_finder->setPoweredOffFinderMode(enable);
+}
+
+ScopedAStatus BluetoothFinderTest::getPoweredOffFinderMode(bool* status) {
+  return bluetooth_finder->getPoweredOffFinderMode(status);
+}
+
+TEST_P(BluetoothFinderTest, SendEidsSingle) {
+  ScopedAStatus status = sendEids(1);
+  ASSERT_TRUE(status.isOk());
+}
+
+TEST_P(BluetoothFinderTest, Send255Eids) {
+  ScopedAStatus status = sendEids(255);
+  ASSERT_TRUE(status.isOk());
+}
+
+TEST_P(BluetoothFinderTest, setAndGetPoweredOffFinderModeEnable) {
+  ScopedAStatus status = setPoweredOffFinderMode(true);
+  ASSERT_TRUE(status.isOk());
+  bool pof_status;
+  status = getPoweredOffFinderMode(&pof_status);
+  ASSERT_TRUE(status.isOk());
+  ASSERT_TRUE(pof_status);
+}
+
+TEST_P(BluetoothFinderTest, setAndGetPoweredOffFinderModeDisable) {
+  ScopedAStatus status = setPoweredOffFinderMode(false);
+  ASSERT_TRUE(status.isOk());
+  bool pof_status;
+  status = getPoweredOffFinderMode(&pof_status);
+  ASSERT_TRUE(status.isOk());
+  ASSERT_TRUE(!pof_status);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BluetoothFinderTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance, BluetoothFinderTest,
+                         testing::ValuesIn(android::getAidlHalInstanceNames(
+                             IBluetoothFinder::descriptor)),
+                         android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  ABinderProcess_startThreadPool();
+  int status = RUN_ALL_TESTS();
+  ALOGI("Test result = %d", status);
+  return status;
+}
diff --git a/bluetooth/finder/aidl/vts/VtsHalBluetoothFinderTargetTest.xml b/bluetooth/finder/aidl/vts/VtsHalBluetoothFinderTargetTest.xml
new file mode 100644
index 0000000..46053dd
--- /dev/null
+++ b/bluetooth/finder/aidl/vts/VtsHalBluetoothFinderTargetTest.xml
@@ -0,0 +1,33 @@
+<!--
+  Copyright (C) 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<configuration description="Runs VtsHalBluetoothFinderTargetTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalBluetoothFinderTargetTest->/data/local/tmp/VtsHalBluetoothFinderTargetTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalBluetoothFinderTargetTest" />
+    </test>
+</configuration>
diff --git a/broadcastradio/aidl/default/BroadcastRadio.cpp b/broadcastradio/aidl/default/BroadcastRadio.cpp
index 63073d7..4d6d81d 100644
--- a/broadcastradio/aidl/default/BroadcastRadio.cpp
+++ b/broadcastradio/aidl/default/BroadcastRadio.cpp
@@ -48,6 +48,8 @@
 inline constexpr std::chrono::seconds kListDelayTimeS = 1s;
 
 // clang-format off
+const AmFmBandRange kFmFullBandRange = {65000, 108000, 10, 0};
+const AmFmBandRange kAmFullBandRange = {150, 30000, 1, 0};
 const AmFmRegionConfig kDefaultAmFmConfig = {
         {
                 {87500, 108000, 100, 100},  // FM
@@ -64,12 +66,7 @@
 
     prop.maker = "Android";
     prop.product = virtualRadio.getName();
-    prop.supportedIdentifierTypes = vector<IdentifierType>({
-            IdentifierType::AMFM_FREQUENCY_KHZ,
-            IdentifierType::RDS_PI,
-            IdentifierType::HD_STATION_ID_EXT,
-            IdentifierType::DAB_SID_EXT,
-    });
+    prop.supportedIdentifierTypes = virtualRadio.getSupportedIdentifierTypes();
     prop.vendorInfo = vector<VendorKeyValue>({
             {"com.android.sample", "sample"},
     });
@@ -77,14 +74,71 @@
     return prop;
 }
 
+bool isDigitalProgramAllowed(const ProgramSelector& sel, bool forceAnalogFm, bool forceAnalogAm) {
+    if (sel.primaryId.type != IdentifierType::HD_STATION_ID_EXT) {
+        return true;
+    }
+    int32_t freq = static_cast<int32_t>(utils::getAmFmFrequency(sel));
+    bool isFm = freq >= kFmFullBandRange.lowerBound && freq <= kFmFullBandRange.upperBound;
+    return isFm ? !forceAnalogFm : !forceAnalogAm;
+}
+
+/**
+ * Checks whether a program selector is in the current band.
+ *
+ * <p>For an AM/FM program, this method checks whether it is in the current AM/FM band. For a
+ * program selector is also an HD program, it is also checked whether HD radio is enabled in the
+ * current AM/FM band. For a non-AM/FM program, the method will returns {@code true} directly.
+ * @param sel Program selector to be checked
+ * @param currentAmFmBandRange the current AM/FM band
+ * @param forceAnalogFm whether FM band is forced to be analog
+ * @param forceAnalogAm  whether AM band is forced to be analog
+ * @return whether the program selector is in the current band if it is an AM/FM (including HD)
+ * selector, {@code true} otherwise
+ */
+bool isProgramInBand(const ProgramSelector& sel,
+                     const std::optional<AmFmBandRange>& currentAmFmBandRange, bool forceAnalogFm,
+                     bool forceAnalogAm) {
+    if (!utils::hasAmFmFrequency(sel)) {
+        return true;
+    }
+    if (!currentAmFmBandRange.has_value()) {
+        return false;
+    }
+    int32_t freq = static_cast<int32_t>(utils::getAmFmFrequency(sel));
+    if (freq < currentAmFmBandRange->lowerBound || freq > currentAmFmBandRange->upperBound) {
+        return false;
+    }
+    return isDigitalProgramAllowed(sel, forceAnalogFm, forceAnalogAm);
+}
+
 // Makes ProgramInfo that does not point to any particular program
 ProgramInfo makeSampleProgramInfo(const ProgramSelector& selector) {
     ProgramInfo info = {};
     info.selector = selector;
-    info.logicallyTunedTo =
-            utils::makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ,
-                                  utils::getId(selector, IdentifierType::AMFM_FREQUENCY_KHZ));
-    info.physicallyTunedTo = info.logicallyTunedTo;
+    switch (info.selector.primaryId.type) {
+        case IdentifierType::AMFM_FREQUENCY_KHZ:
+            info.logicallyTunedTo = utils::makeIdentifier(
+                    IdentifierType::AMFM_FREQUENCY_KHZ,
+                    utils::getId(selector, IdentifierType::AMFM_FREQUENCY_KHZ));
+            info.physicallyTunedTo = info.logicallyTunedTo;
+            break;
+        case IdentifierType::HD_STATION_ID_EXT:
+            info.logicallyTunedTo = utils::makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ,
+                                                          utils::getAmFmFrequency(info.selector));
+            info.physicallyTunedTo = info.logicallyTunedTo;
+            break;
+        case IdentifierType::DAB_SID_EXT:
+            info.logicallyTunedTo = info.selector.primaryId;
+            info.physicallyTunedTo = utils::makeIdentifier(
+                    IdentifierType::DAB_FREQUENCY_KHZ,
+                    utils::getId(selector, IdentifierType::DAB_FREQUENCY_KHZ));
+            break;
+        default:
+            info.logicallyTunedTo = info.selector.primaryId;
+            info.physicallyTunedTo = info.logicallyTunedTo;
+            break;
+    }
     return info;
 }
 
@@ -112,6 +166,7 @@
         } else {
             mCurrentProgram = sel;
         }
+        adjustAmFmRangeLocked();
     }
 }
 
@@ -124,8 +179,8 @@
     if (full) {
         *returnConfigs = {};
         returnConfigs->ranges = vector<AmFmBandRange>({
-                {65000, 108000, 10, 0},  // FM
-                {150, 30000, 1, 0},      // AM
+                kFmFullBandRange,
+                kAmFullBandRange,
         });
         returnConfigs->fmDeemphasis =
                 AmFmRegionConfig::DEEMPHASIS_D50 | AmFmRegionConfig::DEEMPHASIS_D75;
@@ -171,14 +226,27 @@
 
     VirtualProgram virtualProgram = {};
     ProgramInfo programInfo;
-    if (mVirtualRadio.getProgram(sel, &virtualProgram)) {
+    bool isProgramAllowed =
+            isDigitalProgramAllowed(sel, isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_FM),
+                                    isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_AM));
+    if (isProgramAllowed && mVirtualRadio.getProgram(sel, &virtualProgram)) {
         mCurrentProgram = virtualProgram.selector;
         programInfo = virtualProgram;
     } else {
-        mCurrentProgram = sel;
+        if (!isProgramAllowed) {
+            mCurrentProgram = utils::makeSelectorAmfm(utils::getAmFmFrequency(sel));
+        } else {
+            mCurrentProgram = sel;
+        }
         programInfo = makeSampleProgramInfo(sel);
     }
-    mIsTuneCompleted = true;
+    programInfo.infoFlags |= ProgramInfo::FLAG_SIGNAL_ACQUISITION;
+    if (programInfo.selector.primaryId.type != IdentifierType::HD_STATION_ID_EXT) {
+        mIsTuneCompleted = true;
+    }
+    if (adjustAmFmRangeLocked()) {
+        startProgramListUpdatesLocked({});
+    }
 
     return programInfo;
 }
@@ -206,6 +274,32 @@
     return ScopedAStatus::ok();
 }
 
+void BroadcastRadio::handleProgramInfoUpdateRadioCallback(
+        ProgramInfo programInfo, const std::shared_ptr<ITunerCallback>& callback) {
+    callback->onCurrentProgramInfoChanged(programInfo);
+    if (programInfo.selector.primaryId.type != IdentifierType::HD_STATION_ID_EXT) {
+        return;
+    }
+    ProgramSelector sel = programInfo.selector;
+    auto cancelTask = [sel, callback]() { callback->onTuneFailed(Result::CANCELED, sel); };
+    programInfo.infoFlags |= ProgramInfo::FLAG_HD_SIS_ACQUISITION;
+    auto sisAcquiredTask = [this, callback, programInfo, cancelTask]() {
+        callback->onCurrentProgramInfoChanged(programInfo);
+        auto audioAcquiredTask = [this, callback, programInfo]() {
+            ProgramInfo hdProgramInfoWithAudio = programInfo;
+            hdProgramInfoWithAudio.infoFlags |= ProgramInfo::FLAG_HD_AUDIO_ACQUISITION;
+            callback->onCurrentProgramInfoChanged(hdProgramInfoWithAudio);
+            lock_guard<mutex> lk(mMutex);
+            mIsTuneCompleted = true;
+        };
+        lock_guard<mutex> lk(mMutex);
+        mTuningThread->schedule(audioAcquiredTask, cancelTask, kTuneDelayTimeMs);
+    };
+
+    lock_guard<mutex> lk(mMutex);
+    mTuningThread->schedule(sisAcquiredTask, cancelTask, kTuneDelayTimeMs);
+}
+
 ScopedAStatus BroadcastRadio::tune(const ProgramSelector& program) {
     LOG(DEBUG) << __func__ << ": tune to " << program.toString() << "...";
 
@@ -238,7 +332,7 @@
             lock_guard<mutex> lk(mMutex);
             programInfo = tuneInternalLocked(program);
         }
-        callback->onCurrentProgramInfoChanged(programInfo);
+        handleProgramInfoUpdateRadioCallback(programInfo, callback);
     };
     auto cancelTask = [program, callback]() { callback->onTuneFailed(Result::CANCELED, program); };
     mTuningThread->schedule(task, cancelTask, kTuneDelayTimeMs);
@@ -246,6 +340,122 @@
     return ScopedAStatus::ok();
 }
 
+bool BroadcastRadio::findNextLocked(const ProgramSelector& current, bool directionUp,
+                                    bool skipSubChannel, VirtualProgram* nextProgram) const {
+    if (mProgramList.empty()) {
+        return false;
+    }
+    // The list is not sorted here since it has already stored in VirtualRadio.
+    bool hasAmFmFrequency = utils::hasAmFmFrequency(current);
+    bool hasDabSId = utils::hasId(current, IdentifierType::DAB_SID_EXT);
+    uint32_t currentChannel =
+            hasAmFmFrequency ? utils::getAmFmFrequency(current) : utils::getDabSId(current);
+    auto found =
+            std::lower_bound(mProgramList.begin(), mProgramList.end(), VirtualProgram({current}));
+    if (directionUp) {
+        if (found < mProgramList.end() - 1) {
+            // When seeking up, tuner will jump to the first selector which is main program service
+            // greater than and of the same band as the current program selector in the program
+            // list (if not exist, jump to the first selector in the same band) for skipping
+            // sub-channels case or AM/FM without HD radio enabled case. Otherwise, the tuner will
+            // jump to the first selector which is greater than and of the same band as the current
+            // program selector.
+            if (utils::tunesTo(current, found->selector)) found++;
+            if (skipSubChannel) {
+                if (hasAmFmFrequency || hasDabSId) {
+                    auto firstFound = found;
+                    while ((hasAmFmFrequency &&
+                            utils::getAmFmFrequency(found->selector) == currentChannel) ||
+                           (hasDabSId && utils::getDabSId(found->selector) == currentChannel)) {
+                        if (found < mProgramList.end() - 1) {
+                            found++;
+                        } else {
+                            found = mProgramList.begin();
+                        }
+                        if (found == firstFound) {
+                            // Only one main channel exists in the program list, the tuner cannot
+                            // skip sub-channel to the next program selector.
+                            return false;
+                        }
+                    }
+                }
+            }
+        } else {
+            // If the selector of current program is no less than all selectors of the same band or
+            // not found in the program list, seeking up should wrap the tuner to the first program
+            // selector of the same band in the program list.
+            found = mProgramList.begin();
+        }
+    } else {
+        if (found > mProgramList.begin() && found != mProgramList.end()) {
+            // When seeking down, tuner will jump to the first selector which is main program
+            // service less than and of the same band as the current program selector in the
+            // program list (if not exist, jump to the last main program service selector of the
+            // same band) for skipping sub-channels case or AM/FM without HD radio enabled case.
+            // Otherwise, the tuner will jump to the first selector less than and of the same band
+            // as the current program selector.
+            found--;
+            if ((hasAmFmFrequency && utils::hasAmFmFrequency(found->selector)) ||
+                (hasDabSId && utils::hasId(found->selector, IdentifierType::DAB_SID_EXT))) {
+                uint32_t nextChannel = hasAmFmFrequency ? utils::getAmFmFrequency(found->selector)
+                                                        : utils::getDabSId(found->selector);
+                if (nextChannel != currentChannel) {
+                    jumpToFirstSubChannelLocked(found);
+                } else if (skipSubChannel) {
+                    jumpToFirstSubChannelLocked(found);
+                    auto firstFound = found;
+                    if (found > mProgramList.begin()) {
+                        found--;
+                    } else {
+                        found = mProgramList.end() - 1;
+                    }
+                    jumpToFirstSubChannelLocked(found);
+                    if (found == firstFound) {
+                        // Only one main channel exists in the program list, the tuner cannot skip
+                        // sub-channel to the next program selector.
+                        return false;
+                    }
+                }
+            }
+        } else {
+            // If the selector of current program is no greater than all selectors of the same band
+            // or not found in the program list, seeking down should wrap the tuner to the last
+            // selector of the same band in the program list. If the last program selector in the
+            // program list is sub-channel and skipping sub-channels is needed, the tuner will jump
+            // to the last main program service of the same band in the program list.
+            found = mProgramList.end() - 1;
+            jumpToFirstSubChannelLocked(found);
+        }
+    }
+    *nextProgram = *found;
+    return true;
+}
+
+void BroadcastRadio::jumpToFirstSubChannelLocked(vector<VirtualProgram>::const_iterator& it) const {
+    if (it == mProgramList.begin()) {
+        return;
+    }
+    bool hasAmFmFrequency = utils::hasAmFmFrequency(it->selector);
+    bool hasDabSId = utils::hasId(it->selector, IdentifierType::DAB_SID_EXT);
+    if (hasAmFmFrequency || hasDabSId) {
+        uint32_t currentChannel = hasAmFmFrequency ? utils::getAmFmFrequency(it->selector)
+                                                   : utils::getDabSId(it->selector);
+        it--;
+        while (it != mProgramList.begin()) {
+            if (hasAmFmFrequency && utils::hasAmFmFrequency(it->selector) &&
+                utils::getAmFmFrequency(it->selector) == currentChannel) {
+                it--;
+            } else if (hasDabSId && utils::hasId(it->selector, IdentifierType::DAB_SID_EXT) &&
+                       utils::getDabSId(it->selector) == currentChannel) {
+                it--;
+            } else {
+                break;
+            }
+        }
+        it++;
+    }
+}
+
 ScopedAStatus BroadcastRadio::seek(bool directionUp, bool skipSubChannel) {
     LOG(DEBUG) << __func__ << ": seek " << (directionUp ? "up" : "down") << " with skipSubChannel? "
                << (skipSubChannel ? "yes" : "no") << "...";
@@ -259,11 +469,21 @@
 
     cancelLocked();
 
+    auto filterCb = [this](const VirtualProgram& program) {
+        return isProgramInBand(program.selector, mCurrentAmFmBandRange,
+                               isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_FM),
+                               isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_AM));
+    };
     const auto& list = mVirtualRadio.getProgramList();
+    mProgramList.clear();
+    std::copy_if(list.begin(), list.end(), std::back_inserter(mProgramList), filterCb);
     std::shared_ptr<ITunerCallback> callback = mCallback;
     auto cancelTask = [callback]() { callback->onTuneFailed(Result::CANCELED, {}); };
-    if (list.empty()) {
-        mIsTuneCompleted = false;
+
+    VirtualProgram nextProgram = {};
+    bool foundNext = findNextLocked(mCurrentProgram, directionUp, skipSubChannel, &nextProgram);
+    mIsTuneCompleted = false;
+    if (!foundNext) {
         auto task = [callback]() {
             LOG(DEBUG) << "seek: program list is empty, seek couldn't stop";
 
@@ -274,33 +494,13 @@
         return ScopedAStatus::ok();
     }
 
-    // The list is not sorted here since it has already stored in VirtualRadio.
-    // If the list is not sorted in advance, it should be sorted here.
-    const auto& current = mCurrentProgram;
-    auto found = std::lower_bound(list.begin(), list.end(), VirtualProgram({current}));
-    if (directionUp) {
-        if (found < list.end() - 1) {
-            if (tunesTo(current, found->selector)) found++;
-        } else {
-            found = list.begin();
-        }
-    } else {
-        if (found > list.begin() && found != list.end()) {
-            found--;
-        } else {
-            found = list.end() - 1;
-        }
-    }
-    const ProgramSelector tuneTo = found->selector;
-
-    mIsTuneCompleted = false;
-    auto task = [this, tuneTo, callback]() {
+    auto task = [this, nextProgram, callback]() {
         ProgramInfo programInfo = {};
         {
             lock_guard<mutex> lk(mMutex);
-            programInfo = tuneInternalLocked(tuneTo);
+            programInfo = tuneInternalLocked(nextProgram.selector);
         }
-        callback->onCurrentProgramInfoChanged(programInfo);
+        handleProgramInfoUpdateRadioCallback(programInfo, callback);
     };
     mTuningThread->schedule(task, cancelTask, kSeekDelayTimeMs);
 
@@ -319,31 +519,33 @@
 
     cancelLocked();
 
-    if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ)) {
+    int64_t stepTo;
+    if (utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ)) {
+        stepTo = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ);
+    } else if (mCurrentProgram.primaryId.type == IdentifierType::HD_STATION_ID_EXT) {
+        stepTo = utils::getHdFrequency(mCurrentProgram);
+    } else {
         LOG(WARNING) << __func__ << ": can't step in anything else than AM/FM";
         return ScopedAStatus::fromServiceSpecificErrorWithMessage(
                 resultToInt(Result::NOT_SUPPORTED), "cannot step in anything else than AM/FM");
     }
 
-    int64_t stepTo = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ);
-    std::optional<AmFmBandRange> range = getAmFmRangeLocked();
-    if (!range) {
-        LOG(ERROR) << __func__ << ": can't find current band or tune operation is in process";
+    if (!mCurrentAmFmBandRange.has_value()) {
+        LOG(ERROR) << __func__ << ": can't find current band";
         return ScopedAStatus::fromServiceSpecificErrorWithMessage(
-                resultToInt(Result::INTERNAL_ERROR),
-                "can't find current band or tune operation is in process");
+                resultToInt(Result::INTERNAL_ERROR), "can't find current band");
     }
 
     if (directionUp) {
-        stepTo += range->spacing;
+        stepTo += mCurrentAmFmBandRange->spacing;
     } else {
-        stepTo -= range->spacing;
+        stepTo -= mCurrentAmFmBandRange->spacing;
     }
-    if (stepTo > range->upperBound) {
-        stepTo = range->lowerBound;
+    if (stepTo > mCurrentAmFmBandRange->upperBound) {
+        stepTo = mCurrentAmFmBandRange->lowerBound;
     }
-    if (stepTo < range->lowerBound) {
-        stepTo = range->upperBound;
+    if (stepTo < mCurrentAmFmBandRange->lowerBound) {
+        stepTo = mCurrentAmFmBandRange->upperBound;
     }
 
     mIsTuneCompleted = false;
@@ -354,7 +556,7 @@
             lock_guard<mutex> lk(mMutex);
             programInfo = tuneInternalLocked(utils::makeSelectorAmfm(stepTo));
         }
-        callback->onCurrentProgramInfoChanged(programInfo);
+        handleProgramInfoUpdateRadioCallback(programInfo, callback);
     };
     auto cancelTask = [callback]() { callback->onTuneFailed(Result::CANCELED, {}); };
     mTuningThread->schedule(task, cancelTask, kStepDelayTimeMs);
@@ -380,16 +582,14 @@
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus BroadcastRadio::startProgramListUpdates(const ProgramFilter& filter) {
-    LOG(DEBUG) << __func__ << ": requested program list updates, filter = " << filter.toString()
-               << "...";
-
-    auto filterCb = [&filter](const VirtualProgram& program) {
-        return utils::satisfies(filter, program.selector);
+void BroadcastRadio::startProgramListUpdatesLocked(const ProgramFilter& filter) {
+    auto filterCb = [&filter, this](const VirtualProgram& program) {
+        return utils::satisfies(filter, program.selector) &&
+               isProgramInBand(program.selector, mCurrentAmFmBandRange,
+                               isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_FM),
+                               isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_AM));
     };
 
-    lock_guard<mutex> lk(mMutex);
-
     cancelProgramListUpdateLocked();
 
     const auto& list = mVirtualRadio.getProgramList();
@@ -415,6 +615,15 @@
         callback->onProgramListUpdated(chunk);
     };
     mProgramListThread->schedule(task, kListDelayTimeS);
+}
+
+ScopedAStatus BroadcastRadio::startProgramListUpdates(const ProgramFilter& filter) {
+    LOG(DEBUG) << __func__ << ": requested program list updates, filter = " << filter.toString()
+               << "...";
+
+    lock_guard<mutex> lk(mMutex);
+
+    startProgramListUpdatesLocked(filter);
 
     return ScopedAStatus::ok();
 }
@@ -431,18 +640,28 @@
     return ScopedAStatus::ok();
 }
 
+bool BroadcastRadio::isConfigFlagSetLocked(ConfigFlag flag) const {
+    int flagBit = static_cast<int>(flag);
+    return ((mConfigFlagValues >> flagBit) & 1) == 1;
+}
+
 ScopedAStatus BroadcastRadio::isConfigFlagSet(ConfigFlag flag, bool* returnIsSet) {
     LOG(DEBUG) << __func__ << ": flag = " << toString(flag);
 
-    int flagBit = static_cast<int>(flag);
+    if (flag == ConfigFlag::FORCE_ANALOG) {
+        flag = ConfigFlag::FORCE_ANALOG_FM;
+    }
     lock_guard<mutex> lk(mMutex);
-    *returnIsSet = ((mConfigFlagValues >> flagBit) & 1) == 1;
+    *returnIsSet = isConfigFlagSetLocked(flag);
     return ScopedAStatus::ok();
 }
 
 ScopedAStatus BroadcastRadio::setConfigFlag(ConfigFlag flag, bool value) {
     LOG(DEBUG) << __func__ << ": flag = " << toString(flag) << ", value = " << value;
 
+    if (flag == ConfigFlag::FORCE_ANALOG) {
+        flag = ConfigFlag::FORCE_ANALOG_FM;
+    }
     int flagBitMask = 1 << (static_cast<int>(flag));
     lock_guard<mutex> lk(mMutex);
     if (value) {
@@ -450,6 +669,9 @@
     } else {
         mConfigFlagValues &= ~flagBitMask;
     }
+    if (flag == ConfigFlag::FORCE_ANALOG_AM || flag == ConfigFlag::FORCE_ANALOG_FM) {
+        startProgramListUpdatesLocked({});
+    }
     return ScopedAStatus::ok();
 }
 
@@ -468,24 +690,25 @@
     return ScopedAStatus::ok();
 }
 
-std::optional<AmFmBandRange> BroadcastRadio::getAmFmRangeLocked() const {
-    if (!mIsTuneCompleted) {
-        LOG(WARNING) << __func__ << ": tune operation is in process";
-        return {};
-    }
-    if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ)) {
+bool BroadcastRadio::adjustAmFmRangeLocked() {
+    bool hasBandBefore = mCurrentAmFmBandRange.has_value();
+    if (!utils::hasAmFmFrequency(mCurrentProgram)) {
         LOG(WARNING) << __func__ << ": current program does not has AMFM_FREQUENCY_KHZ identifier";
-        return {};
+        mCurrentAmFmBandRange.reset();
+        return hasBandBefore;
     }
 
-    int64_t freq = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ);
+    int32_t freq = static_cast<int32_t>(utils::getAmFmFrequency(mCurrentProgram));
     for (const auto& range : mAmFmConfig.ranges) {
         if (range.lowerBound <= freq && range.upperBound >= freq) {
-            return range;
+            bool isBandChanged = hasBandBefore ? *mCurrentAmFmBandRange != range : true;
+            mCurrentAmFmBandRange = range;
+            return isBandChanged;
         }
     }
 
-    return {};
+    mCurrentAmFmBandRange.reset();
+    return !hasBandBefore;
 }
 
 ScopedAStatus BroadcastRadio::registerAnnouncementListener(
diff --git a/broadcastradio/aidl/default/BroadcastRadio.h b/broadcastradio/aidl/default/BroadcastRadio.h
index 0f818ce..60ea907 100644
--- a/broadcastradio/aidl/default/BroadcastRadio.h
+++ b/broadcastradio/aidl/default/BroadcastRadio.h
@@ -39,21 +39,25 @@
   public:
     explicit BroadcastRadio(const VirtualRadio& virtualRadio);
     ~BroadcastRadio();
-    ndk::ScopedAStatus getAmFmRegionConfig(bool full, AmFmRegionConfig* returnConfigs) override;
+    ndk::ScopedAStatus getAmFmRegionConfig(bool full, AmFmRegionConfig* returnConfigs)
+            EXCLUDES(mMutex) override;
     ndk::ScopedAStatus getDabRegionConfig(std::vector<DabTableEntry>* returnConfigs) override;
     ndk::ScopedAStatus getImage(int32_t id, std::vector<uint8_t>* returnImage) override;
-    ndk::ScopedAStatus getProperties(Properties* returnProperties) override;
+    ndk::ScopedAStatus getProperties(Properties* returnProperties) EXCLUDES(mMutex) override;
 
-    ndk::ScopedAStatus setTunerCallback(const std::shared_ptr<ITunerCallback>& callback) override;
-    ndk::ScopedAStatus unsetTunerCallback() override;
-    ndk::ScopedAStatus tune(const ProgramSelector& program) override;
-    ndk::ScopedAStatus seek(bool directionUp, bool skipSubChannel) override;
-    ndk::ScopedAStatus step(bool directionUp) override;
-    ndk::ScopedAStatus cancel() override;
-    ndk::ScopedAStatus startProgramListUpdates(const ProgramFilter& filter) override;
-    ndk::ScopedAStatus stopProgramListUpdates() override;
-    ndk::ScopedAStatus isConfigFlagSet(ConfigFlag flag, bool* returnIsSet) override;
-    ndk::ScopedAStatus setConfigFlag(ConfigFlag flag, bool in_value) override;
+    ndk::ScopedAStatus setTunerCallback(const std::shared_ptr<ITunerCallback>& callback)
+            EXCLUDES(mMutex) override;
+    ndk::ScopedAStatus unsetTunerCallback() EXCLUDES(mMutex) override;
+    ndk::ScopedAStatus tune(const ProgramSelector& program) EXCLUDES(mMutex) override;
+    ndk::ScopedAStatus seek(bool directionUp, bool skipSubChannel) EXCLUDES(mMutex) override;
+    ndk::ScopedAStatus step(bool directionUp) EXCLUDES(mMutex) override;
+    ndk::ScopedAStatus cancel() EXCLUDES(mMutex) override;
+    ndk::ScopedAStatus startProgramListUpdates(const ProgramFilter& filter)
+            EXCLUDES(mMutex) override;
+    ndk::ScopedAStatus stopProgramListUpdates() EXCLUDES(mMutex) override;
+    ndk::ScopedAStatus isConfigFlagSet(ConfigFlag flag, bool* returnIsSet)
+            EXCLUDES(mMutex) override;
+    ndk::ScopedAStatus setConfigFlag(ConfigFlag flag, bool in_value) EXCLUDES(mMutex) override;
     ndk::ScopedAStatus setParameters(const std::vector<VendorKeyValue>& parameters,
                                      std::vector<VendorKeyValue>* returnParameters) override;
     ndk::ScopedAStatus getParameters(const std::vector<std::string>& keys,
@@ -62,7 +66,7 @@
             const std::shared_ptr<IAnnouncementListener>& listener,
             const std::vector<AnnouncementType>& enabled,
             std::shared_ptr<ICloseHandle>* returnCloseHandle) override;
-    binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
+    binder_status_t dump(int fd, const char** args, uint32_t numArgs) EXCLUDES(mMutex) override;
 
   private:
     const VirtualRadio& mVirtualRadio;
@@ -75,15 +79,26 @@
     bool mIsTuneCompleted GUARDED_BY(mMutex) = true;
     Properties mProperties GUARDED_BY(mMutex);
     ProgramSelector mCurrentProgram GUARDED_BY(mMutex) = {};
+    std::vector<VirtualProgram> mProgramList GUARDED_BY(mMutex) = {};
+    std::optional<AmFmBandRange> mCurrentAmFmBandRange GUARDED_BY(mMutex);
     std::shared_ptr<ITunerCallback> mCallback GUARDED_BY(mMutex);
 
     // Bitmap for all ConfigFlag values
     int mConfigFlagValues GUARDED_BY(mMutex) = 0;
 
-    std::optional<AmFmBandRange> getAmFmRangeLocked() const;
-    void cancelLocked();
-    ProgramInfo tuneInternalLocked(const ProgramSelector& sel);
-    void cancelProgramListUpdateLocked();
+    bool adjustAmFmRangeLocked() REQUIRES(mMutex);
+    void cancelLocked() REQUIRES(mMutex);
+    ProgramInfo tuneInternalLocked(const ProgramSelector& sel) REQUIRES(mMutex);
+    void startProgramListUpdatesLocked(const ProgramFilter& filter) REQUIRES(mMutex);
+    void cancelProgramListUpdateLocked() REQUIRES(mMutex);
+    void handleProgramInfoUpdateRadioCallback(ProgramInfo programInfo,
+                                              const std::shared_ptr<ITunerCallback>& callback)
+            EXCLUDES(mMutex);
+    bool findNextLocked(const ProgramSelector& current, bool directionUp, bool skipSubChannel,
+                        VirtualProgram* nextProgram) const REQUIRES(mMutex);
+    void jumpToFirstSubChannelLocked(std::vector<VirtualProgram>::const_iterator& it) const
+            REQUIRES(mMutex);
+    bool isConfigFlagSetLocked(ConfigFlag flag) const REQUIRES(mMutex);
 
     binder_status_t cmdHelp(int fd) const;
     binder_status_t cmdTune(int fd, const char** args, uint32_t numArgs);
@@ -93,7 +108,7 @@
     binder_status_t cmdStartProgramListUpdates(int fd, const char** args, uint32_t numArgs);
     binder_status_t cmdStopProgramListUpdates(int fd, uint32_t numArgs);
 
-    binder_status_t dumpsys(int fd);
+    binder_status_t dumpsys(int fd) EXCLUDES(mMutex);
 };
 
 }  // namespace aidl::android::hardware::broadcastradio
diff --git a/broadcastradio/aidl/default/VirtualProgram.cpp b/broadcastradio/aidl/default/VirtualProgram.cpp
index 4fe6567..dca431d 100644
--- a/broadcastradio/aidl/default/VirtualProgram.cpp
+++ b/broadcastradio/aidl/default/VirtualProgram.cpp
@@ -49,7 +49,12 @@
             break;
         case IdentifierType::HD_STATION_ID_EXT:
             info.logicallyTunedTo = selectId(IdentifierType::HD_STATION_ID_EXT);
-            info.physicallyTunedTo = selectId(IdentifierType::AMFM_FREQUENCY_KHZ);
+            if (utils::hasId(info.selector, IdentifierType::AMFM_FREQUENCY_KHZ)) {
+                info.physicallyTunedTo = selectId(IdentifierType::AMFM_FREQUENCY_KHZ);
+            } else {
+                info.physicallyTunedTo = utils::makeIdentifier(
+                        IdentifierType::AMFM_FREQUENCY_KHZ, utils::getHdFrequency(info.selector));
+            }
             break;
         case IdentifierType::DAB_SID_EXT:
             info.logicallyTunedTo = selectId(IdentifierType::DAB_SID_EXT);
@@ -91,9 +96,45 @@
     auto& l = lhs.selector;
     auto& r = rhs.selector;
 
-    // Two programs with the same primaryId are considered the same.
-    if (l.primaryId.type != r.primaryId.type) return l.primaryId.type < r.primaryId.type;
+    if ((utils::hasId(l, IdentifierType::AMFM_FREQUENCY_KHZ) ||
+         l.primaryId.type == IdentifierType::HD_STATION_ID_EXT) &&
+        (utils::hasId(r, IdentifierType::AMFM_FREQUENCY_KHZ) ||
+         r.primaryId.type == IdentifierType::HD_STATION_ID_EXT)) {
+        uint32_t freq1 = utils::getAmFmFrequency(l);
+        int subChannel1 = l.primaryId.type == IdentifierType::HD_STATION_ID_EXT
+                                  ? utils::getHdSubchannel(l)
+                                  : 0;
+        uint32_t freq2 = utils::getAmFmFrequency(r);
+        int subChannel2 = r.primaryId.type == IdentifierType::HD_STATION_ID_EXT
+                                  ? utils::getHdSubchannel(r)
+                                  : 0;
+        return freq1 < freq2 || (freq1 == freq2 && (l.primaryId.type < r.primaryId.type ||
+                                                    subChannel1 < subChannel2));
+    } else if (l.primaryId.type == IdentifierType::DAB_SID_EXT &&
+               r.primaryId.type == IdentifierType::DAB_SID_EXT) {
+        uint64_t dabFreq1 = utils::getId(l, IdentifierType::DAB_FREQUENCY_KHZ);
+        uint64_t dabFreq2 = utils::getId(r, IdentifierType::DAB_FREQUENCY_KHZ);
+        if (dabFreq1 != dabFreq2) {
+            return dabFreq1 < dabFreq2;
+        }
+        uint32_t ecc1 = utils::getDabEccCode(l);
+        uint32_t ecc2 = utils::getDabEccCode(r);
+        if (ecc1 != ecc2) {
+            return ecc1 < ecc2;
+        }
+        uint64_t dabEnsemble1 = utils::getId(l, IdentifierType::DAB_ENSEMBLE);
+        uint64_t dabEnsemble2 = utils::getId(r, IdentifierType::DAB_ENSEMBLE);
+        if (dabEnsemble1 != dabEnsemble2) {
+            return dabEnsemble1 < dabEnsemble2;
+        }
+        uint32_t sId1 = utils::getDabSId(l);
+        uint32_t sId2 = utils::getDabSId(r);
+        return sId1 < sId2 || (sId1 == sId2 && utils::getDabSCIdS(l) < utils::getDabSCIdS(r));
+    }
 
+    if (l.primaryId.type != r.primaryId.type) {
+        return l.primaryId.type < r.primaryId.type;
+    }
     return l.primaryId.value < r.primaryId.value;
 }
 
diff --git a/broadcastradio/aidl/default/VirtualRadio.cpp b/broadcastradio/aidl/default/VirtualRadio.cpp
index 86c5a96..d6e58cd 100644
--- a/broadcastradio/aidl/default/VirtualRadio.cpp
+++ b/broadcastradio/aidl/default/VirtualRadio.cpp
@@ -16,12 +16,15 @@
 
 #include "VirtualRadio.h"
 #include <broadcastradio-utils-aidl/Utils.h>
+#include <unordered_set>
 
 namespace aidl::android::hardware::broadcastradio {
 
 using ::aidl::android::hardware::broadcastradio::utils::makeSelectorAmfm;
 using ::aidl::android::hardware::broadcastradio::utils::makeSelectorDab;
+using ::aidl::android::hardware::broadcastradio::utils::makeSelectorHd;
 using ::std::string;
+using ::std::unordered_set;
 using ::std::vector;
 
 VirtualRadio::VirtualRadio(const string& name, const vector<VirtualProgram>& initialList)
@@ -38,15 +41,43 @@
 }
 
 bool VirtualRadio::getProgram(const ProgramSelector& selector, VirtualProgram* programOut) const {
-    for (const auto& program : mPrograms) {
-        if (utils::tunesTo(selector, program.selector)) {
-            *programOut = program;
-            return true;
+    for (auto it = mPrograms.begin(); it != mPrograms.end(); it++) {
+        if (!utils::tunesTo(selector, it->selector)) {
+            continue;
         }
+        auto firstMatchIt = it;
+        if (utils::hasAmFmFrequency(it->selector)) {
+            uint32_t channelFreq = utils::getAmFmFrequency(it->selector);
+            it++;
+            while (it != mPrograms.end() && utils::hasAmFmFrequency(it->selector) &&
+                   utils::getAmFmFrequency(it->selector) == channelFreq) {
+                if (it->selector == selector) {
+                    *programOut = *it;
+                    return true;
+                }
+                it++;
+            }
+        }
+        *programOut = *firstMatchIt;
+        return true;
     }
     return false;
 }
 
+vector<IdentifierType> VirtualRadio::getSupportedIdentifierTypes() const {
+    unordered_set<IdentifierType> supportedIdentifierTypeSet;
+    for (const auto& program : mPrograms) {
+        IdentifierType type = program.selector.primaryId.type;
+        if (supportedIdentifierTypeSet.count(type)) {
+            continue;
+        }
+        supportedIdentifierTypeSet.insert(type);
+    }
+    vector<IdentifierType> supportedIdentifierTypes(supportedIdentifierTypeSet.begin(),
+                                                    supportedIdentifierTypeSet.end());
+    return supportedIdentifierTypes;
+}
+
 // get singleton of AMFM Virtual Radio
 const VirtualRadio& VirtualRadio::getAmFmRadio() {
     // clang-format off
@@ -56,15 +87,27 @@
             {makeSelectorAmfm(/* frequency= */ 94900u), "Wild 94.9", "Drake ft. Rihanna",
                 "Too Good"},
             {makeSelectorAmfm(/* frequency= */ 96500u), "KOIT", "Celine Dion", "All By Myself"},
-            {makeSelectorAmfm(/* frequency= */ 97300u), "Alice@97.3", "Drops of Jupiter", "Train"},
-            {makeSelectorAmfm(/* frequency= */ 99700u), "99.7 Now!", "The Chainsmokers", "Closer"},
             {makeSelectorAmfm(/* frequency= */ 101300u), "101-3 KISS-FM", "Justin Timberlake",
                 "Rock Your Body"},
             {makeSelectorAmfm(/* frequency= */ 103700u), "iHeart80s @ 103.7", "Michael Jackson",
                 "Billie Jean"},
             {makeSelectorAmfm(/* frequency= */ 106100u), "106 KMEL", "Drake", "Marvins Room"},
-            {makeSelectorAmfm(/* frequency= */ 700u), "700 AM", "Artist700", "Title700"},
-            {makeSelectorAmfm(/* frequency= */ 1700u), "1700 AM", "Artist1700", "Title1700"},
+            {makeSelectorAmfm(/* frequency= */ 560u), "Talk Radio 560 KSFO", "Artist560", "Title560"},
+            {makeSelectorAmfm(/* frequency= */ 680u), "KNBR 680", "Artist680", "Title680"},
+            {makeSelectorAmfm(/* frequency= */ 97300u), "Alice@97.3", "Drops of Jupiter", "Train"},
+            {makeSelectorAmfm(/* frequency= */ 99700u), "99.7 Now!", "The Chainsmokers", "Closer"},
+            {makeSelectorHd(/* stationId= */ 0xA0000001u, /* subChannel= */ 0u, /* frequency= */ 97700u),
+                "K-LOVE", "ArtistHd0", "TitleHd0"},
+            {makeSelectorHd(/* stationId= */ 0xA0000001u, /* subChannel= */ 1u, /* frequency= */ 97700u),
+                "Air1", "ArtistHd1", "TitleHd1"},
+            {makeSelectorHd(/* stationId= */ 0xA0000001u, /* subChannel= */ 2u, /* frequency= */ 97700u),
+                "K-LOVE Classics", "ArtistHd2", "TitleHd2"},
+            {makeSelectorHd(/* stationId= */ 0xA0000001u, /* subChannel= */ 0u, /* frequency= */ 98500u),
+                "98.5-1 South Bay's Classic Rock", "ArtistHd0", "TitleHd0"},
+            {makeSelectorHd(/* stationId= */ 0xA0000001u, /* subChannel= */ 1u, /* frequency= */ 98500u),
+                "Highway 1 - Different", "ArtistHd1", "TitleHd1"},
+            {makeSelectorHd(/* stationId= */ 0xB0000001u, /* subChannel= */ 0u, /* frequency= */ 1170u),
+                "KLOK", "ArtistHd1", "TitleHd1"},
         });
     // clang-format on
     return amFmRadioMock;
@@ -76,14 +119,24 @@
     static VirtualRadio dabRadioMock(
         "DAB radio mock",
         {
-            {makeSelectorDab(/* sidExt= */ 0xA000000001u, /* ensemble= */ 0x0001u,
+            {makeSelectorDab(/* sidExt= */ 0x0E10000C221u, /* ensemble= */ 0xCE15u,
                 /* freq= */ 225648u), "BBC Radio 1", "Khalid", "Talk"},
-            {makeSelectorDab(/* sidExt= */ 0xB000000001u, /* ensemble= */ 0x1001u,
+            {makeSelectorDab(/* sidExt= */ 0x0E10000C222u, /* ensemble= */ 0xCE15u,
+                    /* freq= */ 225648u), "BBC Radio 2", "Khalid", "Talk"},
+            {makeSelectorDab(/* sidExt= */ 0xE10000C224u, /* ensemble= */ 0xCE15u,
+                    /* freq= */ 225648u), "BBC Radio 4", "ArtistBBC1", "TitleCountry1"},
+            {makeSelectorDab(/* sidExt= */ 0x1E10000C224u, /* ensemble= */ 0xCE15u,
+                    /* freq= */ 225648u), "BBC Radio 4 LW", "ArtistBBC2", "TitleCountry2"},
+            {makeSelectorDab(/* sidExt= */ 0x0E10000C21Au, /* ensemble= */ 0xC181u,
                 /* freq= */ 222064u), "Classic FM", "Jean Sibelius", "Andante Festivo"},
-            {makeSelectorDab(/* sidExt= */ 0xB000000002u, /* ensemble= */ 0x1002u,
-                /* freq= */ 227360u), "Absolute Radio", "Coldplay", "Clocks"},
-            {makeSelectorDab(/* sidExt= */ 0xB000000002u, /* ensemble= */ 0x1002u,
+            {makeSelectorDab(/* sidExt= */ 0x0E10000C1C0u, /* ensemble= */ 0xC181u,
+                /* freq= */ 223936u), "Absolute Radio", "Coldplay", "Clocks"},
+            {makeSelectorDab(/* sidExt= */ 0x0E10000C1C0u, /* ensemble= */ 0xC181u,
                 /* freq= */ 222064u), "Absolute Radio", "Coldplay", "Clocks"},
+            {makeSelectorDab(/* sidExt= */ 0x0E10000CCE7u, /* ensemble= */ 0xC19Du,
+                    /* freq= */ 218640u), "Absolute Radio Country", "ArtistCountry1", "TitleCountry1"},
+            {makeSelectorDab(/* sidExt= */ 0x0E10000CCE7u, /* ensemble= */ 0xC1A0u,
+                    /* freq= */ 218640u), "Absolute Radio Country", "ArtistCountry2", "TitleCountry2"},
         });
     // clang-format on
     return dabRadioMock;
diff --git a/broadcastradio/aidl/default/VirtualRadio.h b/broadcastradio/aidl/default/VirtualRadio.h
index ae039c4..0d70aef 100644
--- a/broadcastradio/aidl/default/VirtualRadio.h
+++ b/broadcastradio/aidl/default/VirtualRadio.h
@@ -36,6 +36,7 @@
     std::string getName() const;
     const std::vector<VirtualProgram>& getProgramList() const;
     bool getProgram(const ProgramSelector& selector, VirtualProgram* program) const;
+    std::vector<IdentifierType> getSupportedIdentifierTypes() const;
 
     static const VirtualRadio& getAmFmRadio();
     static const VirtualRadio& getDabRadio();
diff --git a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
index b6fb33f..bb43903 100644
--- a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
+++ b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
@@ -139,6 +139,7 @@
 ProgramSelector makeSelectorAmfm(uint32_t frequency);
 ProgramSelector makeSelectorDab(uint64_t sidExt);
 ProgramSelector makeSelectorDab(uint64_t sidExt, uint32_t ensemble, uint64_t freq);
+ProgramSelector makeSelectorHd(uint64_t stationId, uint64_t subChannel, uint64_t frequency);
 
 bool satisfies(const ProgramFilter& filter, const ProgramSelector& sel);
 
@@ -158,6 +159,20 @@
 
 ProgramIdentifier makeHdRadioStationName(const std::string& name);
 
+uint32_t getHdFrequency(const ProgramSelector& sel);
+
+int getHdSubchannel(const ProgramSelector& sel);
+
+uint32_t getDabSId(const ProgramSelector& sel);
+
+int getDabEccCode(const ProgramSelector& sel);
+
+int getDabSCIdS(const ProgramSelector& sel);
+
+bool hasAmFmFrequency(const ProgramSelector& sel);
+
+uint32_t getAmFmFrequency(const ProgramSelector& sel);
+
 template <typename aidl_type>
 inline std::string vectorToString(const std::vector<aidl_type>& in_values) {
     return std::accumulate(std::begin(in_values), std::end(in_values), std::string{},
diff --git a/broadcastradio/common/utilsaidl/src/Utils.cpp b/broadcastradio/common/utilsaidl/src/Utils.cpp
index 2875318..76c3c6a 100644
--- a/broadcastradio/common/utilsaidl/src/Utils.cpp
+++ b/broadcastradio/common/utilsaidl/src/Utils.cpp
@@ -49,12 +49,6 @@
     return getId(a, type) == getId(b, type);
 }
 
-int getHdSubchannel(const ProgramSelector& sel) {
-    int64_t hdSidExt = getId(sel, IdentifierType::HD_STATION_ID_EXT, /* defaultValue */ 0);
-    hdSidExt >>= 32;        // Station ID number
-    return hdSidExt & 0xF;  // HD Radio subchannel
-}
-
 bool maybeGetId(const ProgramSelector& sel, const IdentifierType& type, int64_t* val) {
     // iterate through primaryId and secondaryIds
     for (auto it = begin(sel); it != end(sel); it++) {
@@ -132,8 +126,13 @@
         case IdentifierType::AMFM_FREQUENCY_KHZ:
             if (haveEqualIds(a, b, IdentifierType::HD_STATION_ID_EXT)) return true;
             if (haveEqualIds(a, b, IdentifierType::RDS_PI)) return true;
-            return getHdSubchannel(b) == 0 &&
-                   haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY_KHZ);
+            if (getHdSubchannel(b) != 0) {  // supplemental program services
+                return false;
+            }
+            return haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY_KHZ) ||
+                   (b.primaryId.type == IdentifierType::HD_STATION_ID_EXT &&
+                    static_cast<uint32_t>(getId(a, IdentifierType::AMFM_FREQUENCY_KHZ)) ==
+                            getAmFmFrequency(b));
         case IdentifierType::DAB_SID_EXT:
             if (!haveEqualIds(a, b, IdentifierType::DAB_SID_EXT)) {
                 return false;
@@ -316,6 +315,13 @@
     return sel;
 }
 
+ProgramSelector makeSelectorHd(uint64_t stationId, uint64_t subChannel, uint64_t frequency) {
+    ProgramSelector sel = {};
+    uint64_t sidExt = stationId | (subChannel << 32) | (frequency << 36);
+    sel.primaryId = makeIdentifier(IdentifierType::HD_STATION_ID_EXT, sidExt);
+    return sel;
+}
+
 ProgramSelector makeSelectorDab(uint64_t sidExt, uint32_t ensemble, uint64_t freq) {
     ProgramSelector sel = {};
     sel.primaryId = makeIdentifier(IdentifierType::DAB_SID_EXT, sidExt);
@@ -483,6 +489,47 @@
     return static_cast<IdentifierType>(typeAsInt);
 }
 
+uint32_t getDabSId(const ProgramSelector& sel) {
+    int64_t dabSidExt = getId(sel, IdentifierType::DAB_SID_EXT, /* defaultValue */ 0);
+    return static_cast<uint32_t>(dabSidExt & 0xFFFFFFFF);
+}
+
+int getDabEccCode(const ProgramSelector& sel) {
+    int64_t dabSidExt = getId(sel, IdentifierType::DAB_SID_EXT, /* defaultValue */ 0);
+    return static_cast<uint32_t>((dabSidExt >> 32) & 0xFF);
+}
+
+int getDabSCIdS(const ProgramSelector& sel) {
+    int64_t dabSidExt = getId(sel, IdentifierType::DAB_SID_EXT, /* defaultValue */ 0);
+    return static_cast<uint32_t>((dabSidExt >> 40) & 0xF);
+}
+
+int getHdSubchannel(const ProgramSelector& sel) {
+    int64_t hdSidExt = getId(sel, IdentifierType::HD_STATION_ID_EXT, kValueForNotFoundIdentifier);
+    hdSidExt >>= 32;        // Station ID number
+    return hdSidExt & 0xF;  // HD Radio subchannel
+}
+
+uint32_t getHdFrequency(const ProgramSelector& sel) {
+    int64_t hdSidExt = getId(sel, IdentifierType::HD_STATION_ID_EXT, kValueForNotFoundIdentifier);
+    if (hdSidExt == kValueForNotFoundIdentifier) {
+        return kValueForNotFoundIdentifier;
+    }
+    return static_cast<uint32_t>((hdSidExt >> 36) & 0x3FFFF);  // HD Radio subchannel
+}
+
+bool hasAmFmFrequency(const ProgramSelector& sel) {
+    return hasId(sel, IdentifierType::AMFM_FREQUENCY_KHZ) ||
+           sel.primaryId.type == IdentifierType::HD_STATION_ID_EXT;
+}
+
+uint32_t getAmFmFrequency(const ProgramSelector& sel) {
+    if (hasId(sel, IdentifierType::AMFM_FREQUENCY_KHZ)) {
+        return static_cast<uint32_t>(getId(sel, IdentifierType::AMFM_FREQUENCY_KHZ));
+    }
+    return getHdFrequency(sel);
+}
+
 bool parseArgInt(const std::string& s, int* out) {
     return ::android::base::ParseInt(s, out);
 }
diff --git a/compatibility_matrices/Android.bp b/compatibility_matrices/Android.bp
index 712f28a..b3ca293 100644
--- a/compatibility_matrices/Android.bp
+++ b/compatibility_matrices/Android.bp
@@ -31,7 +31,6 @@
         "kernel_config_q_4.14",
         "kernel_config_q_4.19",
     ],
-    core_hals: "only",
 }
 
 vintf_compatibility_matrix {
@@ -45,7 +44,6 @@
         "kernel_config_r_4.19",
         "kernel_config_r_5.4",
     ],
-    core_hals: "only",
 }
 
 vintf_compatibility_matrix {
@@ -59,7 +57,6 @@
         "kernel_config_s_5.4",
         "kernel_config_s_5.10",
     ],
-    core_hals: "only",
 }
 
 vintf_compatibility_matrix {
@@ -72,7 +69,6 @@
         "kernel_config_t_5.10",
         "kernel_config_t_5.15",
     ],
-    core_hals: "only",
 }
 
 vintf_compatibility_matrix {
@@ -85,7 +81,6 @@
         "kernel_config_u_5.15",
         "kernel_config_u_6.1",
     ],
-    core_hals: "only",
 }
 
 vintf_compatibility_matrix {
@@ -98,5 +93,4 @@
         "kernel_config_v_5.15",
         "kernel_config_v_6.1",
     ],
-    core_hals: "only",
 }
diff --git a/compatibility_matrices/build/vintf_compatibility_matrix.go b/compatibility_matrices/build/vintf_compatibility_matrix.go
index 4f342b2..c72cbde 100644
--- a/compatibility_matrices/build/vintf_compatibility_matrix.go
+++ b/compatibility_matrices/build/vintf_compatibility_matrix.go
@@ -35,10 +35,10 @@
 	pctx = android.NewPackageContext("android/vintf")
 
 	assembleVintfRule = pctx.AndroidStaticRule("assemble_vintf", blueprint.RuleParams{
-		Command:     `${assembleVintfCmd} -i ${inputs} -o ${out} ${extraParams}`,
+		Command:     `${assembleVintfCmd} -i ${inputs} -o ${out}`,
 		CommandDeps: []string{"${assembleVintfCmd}"},
 		Description: "assemble_vintf -i ${inputs}",
-	}, "inputs", "extraParams")
+	}, "inputs")
 
 	xmllintXsd = pctx.AndroidStaticRule("xmllint-xsd", blueprint.RuleParams{
 		Command:     `$XmlLintCmd --quiet --schema $xsd $in > /dev/null && touch -a $out`,
@@ -64,13 +64,6 @@
 
 	// list of kernel_config modules to be combined to final output
 	Kernel_configs []string
-
-	// Default is "default" for compatibility matrices on /vendor
-	// and /odm, and "disallow" for compatibility matrices on /system,
-	// /product, and /system_ext.
-	// If value is "only", only android.* HALs are allowed. If value
-	// is "disallow", none of android.* HALs are allowed.
-	Core_hals *string
 }
 
 type vintfCompatibilityMatrixRule struct {
@@ -173,8 +166,7 @@
 		Implicits:   inputPaths,
 		Output:      g.genFile,
 		Args: map[string]string{
-			"inputs":      strings.Join(inputPaths.Strings(), ":"),
-			"extraParams": strings.Join(g.getExtraParams(), " "),
+			"inputs": strings.Join(inputPaths.Strings(), ":"),
 		},
 	})
 	g.generateValidateBuildAction(ctx, g.genFile, schema.Path())
@@ -199,23 +191,3 @@
 		},
 	}
 }
-
-// Return extra parameters to assemble_vintf.
-func (g *vintfCompatibilityMatrixRule) getExtraParams() []string {
-	var extraParams []string
-
-	coreHalsStrategy := proptools.StringDefault(
-		g.properties.Core_hals,
-		g.defaultCoreHalsStrategy(),
-	)
-	extraParams = append(extraParams, "--core-hals", proptools.ShellEscape(coreHalsStrategy))
-	return extraParams
-}
-
-func (g *vintfCompatibilityMatrixRule) defaultCoreHalsStrategy() string {
-	// TODO(b/290408770): default to "disallow" for FCMs
-
-	// For Device (vendor, odm) compatibility matrix, default is
-	// to not check anything.
-	return "default"
-}
diff --git a/compatibility_matrices/compatibility_matrix.9.xml b/compatibility_matrices/compatibility_matrix.9.xml
index 2a8b772..560766e 100644
--- a/compatibility_matrices/compatibility_matrix.9.xml
+++ b/compatibility_matrices/compatibility_matrix.9.xml
@@ -157,6 +157,14 @@
         </interface>
     </hal>
     <hal format="aidl" optional="true">
+        <name>android.hardware.bluetooth.finder</name>
+        <version>1</version>
+        <interface>
+            <name>IBluetoothFinder</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
         <name>android.hardware.boot</name>
         <interface>
             <name>IBootControl</name>
@@ -304,6 +312,14 @@
             <instance>default</instance>
         </interface>
     </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.security.secretkeeper</name>
+        <version>1</version>
+        <interface>
+            <name>ISecretkeeper</name>
+            <instance>nonsecure</instance>
+        </interface>
+    </hal>
     <hal format="aidl" optional="true" updatable-via-apex="true">
         <name>android.hardware.security.keymint</name>
         <version>1-3</version>
@@ -672,7 +688,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.wifi.hostapd</name>
-        <version>1</version>
+        <version>1-2</version>
         <interface>
             <name>IHostapd</name>
             <instance>default</instance>
diff --git a/gnss/aidl/android/hardware/gnss/IGnss.aidl b/gnss/aidl/android/hardware/gnss/IGnss.aidl
index aaafe7f..8a22d6e 100644
--- a/gnss/aidl/android/hardware/gnss/IGnss.aidl
+++ b/gnss/aidl/android/hardware/gnss/IGnss.aidl
@@ -217,6 +217,10 @@
      * Starts a location output stream using the IGnssCallback gnssLocationCb(), following the
      * settings from the most recent call to setPositionMode().
      *
+     * When a location output stream is in progress, calling setPositionMode() does not change the
+     * settings of the current location output stream. stop() and start() must be called to make the
+     * new settings effective.
+     *
      * This output must operate independently of any GNSS location batching operations,
      * see the IGnssBatching for details.
      */
@@ -306,6 +310,10 @@
     /**
      * Sets the GnssPositionMode parameter, its associated recurrence value, the time between fixes,
      * requested fix accuracy, time to first fix.
+     *
+     * If a location output stream is in progress, calling this method does not affect the settings
+     * of current location output stream. stop() and start() must be called to make the new settings
+     * effective.
      */
     void setPositionMode(in PositionModeOptions options);
 
diff --git a/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/DisplayHotplugEvent.aidl b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/DisplayHotplugEvent.aidl
new file mode 100644
index 0000000..63dca0a
--- /dev/null
+++ b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/DisplayHotplugEvent.aidl
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2023, 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.graphics.common;
+@Backing(type="int") @VintfStability
+enum DisplayHotplugEvent {
+  CONNECTED = 0,
+  DISCONNECTED = 1,
+  ERROR_UNKNOWN = (-1) /* -1 */,
+  ERROR_INCOMPATIBLE_CABLE = (-2) /* -2 */,
+  ERROR_TOO_MANY_DISPLAYS = (-3) /* -3 */,
+}
diff --git a/graphics/common/aidl/android/hardware/graphics/common/DisplayHotplugEvent.aidl b/graphics/common/aidl/android/hardware/graphics/common/DisplayHotplugEvent.aidl
new file mode 100644
index 0000000..b35ada5
--- /dev/null
+++ b/graphics/common/aidl/android/hardware/graphics/common/DisplayHotplugEvent.aidl
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2023, 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.graphics.common;
+
+/**
+ * Display hotplug events through onHotplugEvent callback.
+ */
+@VintfStability
+@Backing(type="int")
+enum DisplayHotplugEvent {
+    /**
+     * Display is successfully connected.
+     * Connected may be called more than once and the behavior of subsequent
+     * calls is that SurfaceFlinger queries the display properties again.
+     */
+    CONNECTED = 0,
+
+    /** Display is successfully disconnected */
+    DISCONNECTED = 1,
+
+    /** Display is plugged in, but an unknown error occurred */
+    ERROR_UNKNOWN = -1,
+
+    /** Display is plugged in, but incompatible cable error detected */
+    ERROR_INCOMPATIBLE_CABLE = -2,
+
+    /**
+     * Display is plugged in, but exceeds the max number of
+     * displays that can be simultaneously connected
+     */
+    ERROR_TOO_MANY_DISPLAYS = -3,
+}
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerCallback.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerCallback.aidl
index 2c08cbe..e64bd52 100644
--- a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerCallback.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerCallback.aidl
@@ -34,6 +34,9 @@
 package android.hardware.graphics.composer3;
 @VintfStability
 interface IComposerCallback {
+  /**
+   * @deprecated : Use instead onHotplugEvent
+   */
   void onHotplug(long display, boolean connected);
   oneway void onRefresh(long display);
   oneway void onSeamlessPossible(long display);
@@ -41,4 +44,5 @@
   oneway void onVsyncPeriodTimingChanged(long display, in android.hardware.graphics.composer3.VsyncPeriodChangeTimeline updatedTimeline);
   oneway void onVsyncIdle(long display);
   oneway void onRefreshRateChangedDebug(in android.hardware.graphics.composer3.RefreshRateChangedDebugData data);
+  void onHotplugEvent(long display, android.hardware.graphics.common.DisplayHotplugEvent event);
 }
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerCallback.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerCallback.aidl
index f4384b7..96eccd7 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerCallback.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerCallback.aidl
@@ -16,6 +16,7 @@
 
 package android.hardware.graphics.composer3;
 
+import android.hardware.graphics.common.DisplayHotplugEvent;
 import android.hardware.graphics.composer3.RefreshRateChangedDebugData;
 import android.hardware.graphics.composer3.VsyncPeriodChangeTimeline;
 
@@ -38,6 +39,7 @@
      * @param display is the display that triggers the hotplug event.
      * @param connected indicates whether the display is connected or
      *        disconnected.
+     * @deprecated: Use instead onHotplugEvent
      */
     void onHotplug(long display, boolean connected);
 
@@ -118,4 +120,23 @@
      * @param data is the data for the callback when refresh rate changed.
      */
     oneway void onRefreshRateChangedDebug(in RefreshRateChangedDebugData data);
+
+    /**
+     * Notifies the client that a DisplayHotplugEvent has occurred for the
+     * given display. Every active display (even a built-in physical display)
+     * must trigger at least one hotplug notification, even if it only occurs
+     * immediately after callback registration.
+     *
+     * Displays which have been connected are assumed to be in PowerMode.OFF,
+     * and the onVsync callback should not be called for a display until vsync
+     * has been enabled with setVsyncEnabled.
+     *
+     * The client may call back into the device while the callback is in
+     * progress. The device must serialize calls to this callback such that
+     * only one thread is calling it at a time.
+     *
+     * @param display is the display that triggers the hotplug event.
+     * @param event is the type of event that occurred.
+     */
+    void onHotplugEvent(long display, DisplayHotplugEvent event);
 }
diff --git a/graphics/composer/aidl/vts/GraphicsComposerCallback.cpp b/graphics/composer/aidl/vts/GraphicsComposerCallback.cpp
index 7b3a2b4..544f692 100644
--- a/graphics/composer/aidl/vts/GraphicsComposerCallback.cpp
+++ b/graphics/composer/aidl/vts/GraphicsComposerCallback.cpp
@@ -17,6 +17,7 @@
 #include "GraphicsComposerCallback.h"
 #include <log/log_main.h>
 #include <utils/Timers.h>
+#include <cinttypes>
 
 #pragma push_macro("LOG_TAG")
 #undef LOG_TAG
@@ -193,4 +194,18 @@
     return ::ndk::ScopedAStatus::ok();
 }
 
+::ndk::ScopedAStatus GraphicsComposerCallback::onHotplugEvent(int64_t in_display,
+                                                              common::DisplayHotplugEvent event) {
+    switch (event) {
+        case common::DisplayHotplugEvent::CONNECTED:
+            return onHotplug(in_display, true);
+        case common::DisplayHotplugEvent::DISCONNECTED:
+            return onHotplug(in_display, false);
+        default:
+            ALOGE("%s(): display:%" PRIu64 ", event:%d", __func__, in_display,
+                  static_cast<int32_t>(event));
+            return ::ndk::ScopedAStatus::ok();
+    }
+}
+
 }  // namespace aidl::android::hardware::graphics::composer3::vts
diff --git a/graphics/composer/aidl/vts/GraphicsComposerCallback.h b/graphics/composer/aidl/vts/GraphicsComposerCallback.h
index 13e992a..7a8d4a3 100644
--- a/graphics/composer/aidl/vts/GraphicsComposerCallback.h
+++ b/graphics/composer/aidl/vts/GraphicsComposerCallback.h
@@ -63,6 +63,8 @@
     virtual ::ndk::ScopedAStatus onVsyncIdle(int64_t in_display) override;
     virtual ::ndk::ScopedAStatus onRefreshRateChangedDebug(
             const RefreshRateChangedDebugData&) override;
+    virtual ::ndk::ScopedAStatus onHotplugEvent(int64_t in_display,
+                                                common::DisplayHotplugEvent) override;
 
     mutable std::mutex mMutex;
     // the set of all currently connected displays
diff --git a/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IGraphicBufferAllocator.aidl b/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IGraphicBufferAllocator.aidl
index 3e460dd..e13ba1f 100644
--- a/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IGraphicBufferAllocator.aidl
+++ b/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IGraphicBufferAllocator.aidl
@@ -39,7 +39,7 @@
   ParcelFileDescriptor getWaitableFd();
   parcelable Allocation {
     android.hardware.HardwareBuffer buffer;
-    ParcelFileDescriptor fence;
+    @nullable ParcelFileDescriptor fence;
   }
   parcelable Description {
     int width;
diff --git a/media/c2/aidl/android/hardware/media/c2/IGraphicBufferAllocator.aidl b/media/c2/aidl/android/hardware/media/c2/IGraphicBufferAllocator.aidl
index 49c4ea4..1710242 100644
--- a/media/c2/aidl/android/hardware/media/c2/IGraphicBufferAllocator.aidl
+++ b/media/c2/aidl/android/hardware/media/c2/IGraphicBufferAllocator.aidl
@@ -35,7 +35,7 @@
      */
     parcelable Allocation {
         HardwareBuffer buffer;
-        ParcelFileDescriptor fence;
+        @nullable ParcelFileDescriptor fence;
     }
 
     /**
diff --git a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl
index b8a0710..46eca69 100644
--- a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl
+++ b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl
@@ -52,4 +52,5 @@
   GAME,
   GAME_LOADING,
   DISPLAY_CHANGE,
+  AUTOMOTIVE_PROJECTION,
 }
diff --git a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/WorkDuration.aidl b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/WorkDuration.aidl
index e86cd40..45013dd 100644
--- a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/WorkDuration.aidl
+++ b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/WorkDuration.aidl
@@ -36,4 +36,7 @@
 parcelable WorkDuration {
   long timeStampNanos;
   long durationNanos;
+  long workPeriodStartTimestampNanos;
+  long cpuDurationNanos;
+  long gpuDurationNanos;
 }
diff --git a/power/aidl/android/hardware/power/Mode.aidl b/power/aidl/android/hardware/power/Mode.aidl
index 78f0363..a8fba72 100644
--- a/power/aidl/android/hardware/power/Mode.aidl
+++ b/power/aidl/android/hardware/power/Mode.aidl
@@ -173,4 +173,10 @@
      * or switching between inner and outer panels.
      */
     DISPLAY_CHANGE,
+
+    /**
+     * This mode indicates that the device is rendering to a remote display in
+     * a vehicle (such as Android Auto projection screen).
+     */
+    AUTOMOTIVE_PROJECTION,
 }
diff --git a/power/aidl/android/hardware/power/WorkDuration.aidl b/power/aidl/android/hardware/power/WorkDuration.aidl
index a06a058..fcd638b 100644
--- a/power/aidl/android/hardware/power/WorkDuration.aidl
+++ b/power/aidl/android/hardware/power/WorkDuration.aidl
@@ -23,8 +23,30 @@
      * sample was measured.
      */
     long timeStampNanos;
+
     /**
-     * Work duration in nanoseconds.
+     * Total work duration in nanoseconds.
      */
     long durationNanos;
+
+    /**
+     * Timestamp in nanoseconds based on CLOCK_MONOTONIC when the work starts.
+     * The work period start timestamp could be zero if the call is from
+     * the legacy SDK/NDK reportActualWorkDuration API.
+     */
+    long workPeriodStartTimestampNanos;
+
+    /**
+     * CPU work duration in nanoseconds.
+     * The CPU work duration could be the same as the total work duration if
+     * the call is from the legacy SDK/NDK reportActualWorkDuration API.
+     */
+    long cpuDurationNanos;
+
+    /**
+     * GPU work duration in nanoseconds.
+     * The GPU work duration could be zero if the call is from the legacy
+     * SDK/NDK reportActualWorkDuration API.
+     */
+    long gpuDurationNanos;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/NasProtocolMessage.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/NasProtocolMessage.aidl
index 9f852cc..4fbc802 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/NasProtocolMessage.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/NasProtocolMessage.aidl
@@ -44,4 +44,7 @@
   AUTHENTICATION_AND_CIPHERING_RESPONSE = 6,
   REGISTRATION_REQUEST = 7,
   DEREGISTRATION_REQUEST = 8,
+  CM_REESTABLISHMENT_REQUEST = 9,
+  CM_SERVICE_REQUEST = 10,
+  IMSI_DETACH_INDICATION = 11,
 }
diff --git a/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl b/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl
index 32890ec..e2df8dd 100644
--- a/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl
+++ b/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl
@@ -669,7 +669,7 @@
      * Note: Cellular identifiers disclosed in uplink messages covered under a NAS Security Context
      * as well as identifiers disclosed in downlink messages are out of scope.
      *
-     * This feature applies to 2g, 3g, 4g, and 5g (SA and NSA) messages sent before a NAS security
+     * This feature applies to 2g, 3g, 4g, and 5g (SA and NSA) messages sent before a security
      * context is established. In scope message definitions and their associated spec references can
      * be found in NasProtocolMessage.
      *
@@ -678,8 +678,6 @@
      * re-enables this functionality. The modem may choose to stop tracking cellular identifiers in
      * the clear during this time.
      *
-     * Note: The default value of enabled shall be true.
-     *
      * @param serial Serial number of request
      * @param enabled Whether or not to enable sending indications for cellular identifiers in the
      *         clear
@@ -694,8 +692,6 @@
      * Enables or disables security algorithm update reports via indication API
      * {@link IRadioNetworkIndication.securityAlgorithmsUpdated()}.
      *
-     * Note: The default value shall be enabled.
-     *
      * @param serial Serial number of request.
      * @param enable {@code true} to enable security algorithm update reports, {@code false} to
      *         disable.
diff --git a/radio/aidl/android/hardware/radio/network/NasProtocolMessage.aidl b/radio/aidl/android/hardware/radio/network/NasProtocolMessage.aidl
index e8d8047..1225c41 100644
--- a/radio/aidl/android/hardware/radio/network/NasProtocolMessage.aidl
+++ b/radio/aidl/android/hardware/radio/network/NasProtocolMessage.aidl
@@ -54,5 +54,14 @@
     REGISTRATION_REQUEST = 7,
     // Reference: 3GPP TS 24.501 8.2.12
     // Applies to 5g networks
-    DEREGISTRATION_REQUEST = 8
+    DEREGISTRATION_REQUEST = 8,
+    // Reference: 3GPP TS 24.008 9.2.4
+    // Applies to 2g and 3g networks
+    CM_REESTABLISHMENT_REQUEST = 9,
+    // Reference: 3GPP TS 24.008 9.2.9
+    // Applies to 2g and 3g networks
+    CM_SERVICE_REQUEST = 10,
+    // Reference: 3GPP TS 24.008 9.2.14
+    // Applies to 2g and 3g networks. Used for circuit-switched detach.
+    IMSI_DETACH_INDICATION = 11
 }
diff --git a/radio/aidl/vts/radio_network_test.cpp b/radio/aidl/vts/radio_network_test.cpp
index c893553..a6ee2a6 100644
--- a/radio/aidl/vts/radio_network_test.cpp
+++ b/radio/aidl/vts/radio_network_test.cpp
@@ -2372,9 +2372,16 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_network->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_network->rspInfo.serial);
 
-    ASSERT_TRUE(CheckAnyOfErrors(
-            radioRsp_network->rspInfo.error,
-            {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
+    if (aidl_version >= 3 && deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_network->rspInfo.error,
+                {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
+    } else {
+        // For aidl_version 2, API is optional
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_network->rspInfo.error,
+                                     {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE,
+                                      RadioError::MODEM_ERR, RadioError::REQUEST_NOT_SUPPORTED}));
+    }
 }
 
 /**
@@ -2406,9 +2413,16 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_network->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_network->rspInfo.serial);
 
-    ASSERT_TRUE(CheckAnyOfErrors(
-            radioRsp_network->rspInfo.error,
-            {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
+    if (aidl_version >= 3 && deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_network->rspInfo.error,
+                {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
+    } else {
+        // For aidl_version 2, API is optional
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_network->rspInfo.error,
+                                     {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE,
+                                      RadioError::MODEM_ERR, RadioError::REQUEST_NOT_SUPPORTED}));
+    }
 }
 
 TEST_P(RadioNetworkTest, isCellularIdentifierTransparencyEnabled) {
@@ -2433,10 +2447,6 @@
     ASSERT_TRUE(CheckAnyOfErrors(
             radioRsp_network->rspInfo.error,
             {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
-
-    // the default value should be true if we have not called the setter
-    EXPECT_TRUE(radioRsp_network->isCellularIdentifierTransparencyEnabled);
-
 }
 
 TEST_P(RadioNetworkTest, setCellularIdentifierTransparencyEnabled) {
@@ -2567,7 +2577,4 @@
     ASSERT_TRUE(CheckAnyOfErrors(
             radioRsp_network->rspInfo.error,
             {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
-
-    // the default value should be true if we have not called the setter
-    EXPECT_TRUE(radioRsp_network->isSecurityAlgorithmsUpdatedEnabled);
 }
diff --git a/renderscript/1.0/default/Android.bp b/renderscript/1.0/default/Android.bp
index c68e370..23fa252 100644
--- a/renderscript/1.0/default/Android.bp
+++ b/renderscript/1.0/default/Android.bp
@@ -27,6 +27,12 @@
         "android.hardware.renderscript@1.0",
     ],
 
+    runtime_libs: [
+        "libRS_internal",
+        //TODO(b/313564579) Install libRSDriver as dependency of libRS_internal
+        "libRSDriver",
+    ],
+
     product_variables: {
         override_rs_driver: {
             cflags: ["-DOVERRIDE_RS_DRIVER=%s"],
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/Arc.cddl b/security/authgraph/aidl/android/hardware/security/authgraph/Arc.cddl
index 4c1b965..0bc39d6 100644
--- a/security/authgraph/aidl/android/hardware/security/authgraph/Arc.cddl
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/Arc.cddl
@@ -28,19 +28,19 @@
     ? -70003 : int,   ; Timestamp in milliseconds since some starting point (generally
                       ; the most recent device boot) which all of the applications within
                       ; the secure domain must agree upon
-    ? -70004 : bstr .size 16,      ; Nonce used in key exchange methods
+    ? -70004 : bstr .size 16,      ; Nonce (a cryptographic random number of 16 bytes) used in key
+                                   ; exchange methods
     ? -70005 : PayloadType,        ; Payload type, if needed to disambiguate, when processing an arc
     ? -70006 : int,                ; Version of the payload structure (if applicable)
     ? -70007 : int,                ; Sequence number (if needed to prevent replay attacks)
     ? -70008 : Direction           ; Direction of the encryption key (i.e. whether it is used to
                                    ; encrypt incoming messages or outgoing messages)
     ? -70009 : bool,               ; "authentication_completed" - this is used during authenticated
-                                   ; key exchange indicate whether signature verification is done
-    ? -70010 : bstr .size 32       ; "session_id" computed during key exchange protocol
+                                   ; key exchange to indicate whether signature verification is done
+    ? -70010 : bstr .size 32       ; "session_id" computed during the key exchange protocol
 }
 
-; Permissions indicate what an arc can be used with. Permissions are added to an arc during the
-; `create()` primitive operation and are propagated during `mint` and `snap` primitive operations.
+; Permissions indicate what an arc can be used with.
 Permission = &(
     -4770552 : IdentityEncoded,  ; "source_id" - in the operations performed by a source, the
                                  ; source adds its own identity to the permissions of an arc.
@@ -54,12 +54,10 @@
                                      ; biometrics.
 )
 
-; Limitations indicate what restrictions are applied on the usage of an arc. Permissions are added
-; to an arc during the `create` primitive operation and are propagated during `snap` primitive
-; operation.
+; Limitations indicate what restrictions are applied on the usage of an arc.
 Limitation = &(
-    -4770554 : bstr,      ; "challenge" - is added to an arc that transfers an auth key to a channel
-                          ; key, in order to ensure the freshness of the authentication.
+    -4770554 : bstr,      ; "challenge" - is added to an arc that encrypts an auth key from a
+                          ; channel key, in order to ensure the freshness of the authentication.
                           ; A challenge is issued by a sink (e.g. Keymint TA, Biometric TAs).
 )
 
@@ -83,7 +81,7 @@
     ; Any other payload formats should also be defined here
 )
 
-SecretKey = &(                     ; One of the payload types of an Arc is a secret key
+SecretKey = &(
     SymmetricKey,
     ECPrivateKey,    ; Private key of a key pair generated for key exchange
 )
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl
index 6ceb09c..a3fb959 100644
--- a/security/authgraph/aidl/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl
@@ -41,8 +41,8 @@
 interface IAuthGraphKeyExchange {
     /**
      * This method is invoked on P1 (source).
-     * Create an ephermeral EC key pair on NIST curve P-256 and a nonce (of 16 bytes) for
-     * key exchange.
+     * Create an ephermeral EC key pair on NIST curve P-256 and a nonce (a cryptographic random
+     * number of 16 bytes) for key exchange.
      *
      * @return SessionInitiationInfo including the `Key` containing the public key of the created
      * key pair and an arc from the per-boot key to the private key, the nonce, the persistent
@@ -52,8 +52,8 @@
      * `SessionInitiationInfo` serves two purposes:
      * i. A mapping to correlate `create` and `finish` calls to P1 in a particular instance of the
      *    key exchange protocol.
-     * ii.A way to minimize the in-memory storage (P1 can include the nonce in the protected headers
-     *    of the arc).
+     * ii.A way to minimize the in-memory storage of P1 allocated for key exchange (P1 can include
+     *    the nonce in the protected headers of the arc).
      * However, P1 should maintain some form of in-memory record to be able to verify that the input
      * `Key` sent to `finish` is from an unfinished instance of a key exchange protocol, to prevent
      * any replay attacks in `finish`.
@@ -66,9 +66,9 @@
      *     0. If either `peerPubKey`, `peerId`, `peerNonce` is not in the expected format, return
      *        errors: INVALID_PEER_KE_KEY, INVALID_IDENTITY, INVALID_PEER_NONCE respectively.
      *     1. Create an ephemeral EC key pair on NIST curve P-256.
-     *     2. Create a nonce (of 16 bytes).
-     *     3. Compute the diffie-hellman shared secret: Z.
-     *     4. Compute a salt = bstr .cbor [
+     *     2. Create a nonce (a cryptographic random number of 16 bytes).
+     *     3. Compute the Diffie-Hellman shared secret: Z.
+     *     4. Compute a salt_input = bstr .cbor [
      *            source_version:    int,                    ; from input `peerVersion`
      *            sink_pub_key:      bstr .cbor PlainPubKey, ; from step #1
      *            source_pub_key:    bstr .cbor PlainPubKey, ; from input `peerPubKey`
@@ -77,7 +77,8 @@
      *            sink_cert_chain:   bstr .cbor ExplicitKeyDiceCertChain, ; from own identity
      *            source_cert_chain: bstr .cbor ExplicitKeyDiceCertChain, ; from input `peerId`
      *        ]
-     *     5. Extract a cryptographic secret S from Z, using the salt from #4 above.
+     *     5. Extract a cryptographic secret S from Z, using the SHA256 digest of the salt_input
+     *        as the salt.
      *     6. Derive two symmetric encryption keys of 256 bits with:
      *        i. b"KE_ENCRYPTION_KEY_SOURCE_TO_SINK" as context for the key used to encrypt incoming
      *           messages
@@ -96,28 +97,29 @@
      *        part of the party's identity.
      *
      * @param peerPubKey - the public key of the key pair created by the peer (P1) for key exchange
+     *                     in `create`
      *
      * @param peerId - the persistent identity of the peer
      *
-     * @param peerNonce - nonce created by the peer
+     * @param peerNonce - nonce created by the peer in `create`
      *
      * @param peerVersion - an integer representing the latest protocol version (i.e. AIDL version)
      *                      supported by the peer
      *
-     * @return KeInitResult including the `Key` containing the public key of the created key pair,
-     * the nonce, the persistent identity, two shared key arcs from step #7, session id, signature
-     * over the session id and the negotiated protocol version. The negotiated protocol version
-     * should be less than or equal to the peer's version.
+     * @return KeInitResult including the `Key` containing the public key of the key pair created in
+     * step #1, the nonce from step #2, the persistent identity of P2, two shared key arcs
+     * from step #7, session id from step #10, signature over the session id from step #11 and the
+     * negotiated protocol version. The negotiated protocol version should be less than or equal to
+     * the `peerVersion`.
      *
-     * Note: The two shared key arcs in the return type: `KeInitResult` serves two purposes:
+     * Note: The two shared key arcs in the return type: `KeInitResult` serve two purposes:
      * i. A mapping to correlate `init` and `authenticationComplete` calls to P2 in a particular
      *    instance of the key exchange protocol.
      * ii.A way to minimize the in-memory storage of P2 allocated for key exchange.
      * However, P2 should maintain some in-memory record to be able to verify that the input
-     * `sharedkeys` sent to `authenticationComplete` and to any subsequent AuthGraph protocol
-     * methods are valid shared keys agreed with the party identified by `peerId`, to prevent
-     * any replay attacks in `authenticationComplete` and in any subsequent AuthGraph protocol
-     * methods which use the shared keys to encrypt the secret messages.
+     * `sharedkeys` sent to `authenticationComplete` are from an unfinished instance of a key
+     * exchange protocol carried out with the party identified by `peerId`, to prevent any replay
+     * attacks in `authenticationComplete`.
      */
     KeInitResult init(
             in PubKey peerPubKey, in Identity peerId, in byte[] peerNonce, in int peerVersion);
@@ -133,8 +135,8 @@
      *        exchange protocol, return error: INVALID_KE_KEY. Similarly, if the public key or the
      *        arc containing the private key in `ownKey` is invalid, return INVALID_PUB_KEY_IN_KEY
      *        and INVALID_PRIV_KEY_ARC_IN_KEY respectively.
-     *     1. Compute the diffie-hellman shared secret: Z.
-     *     2. Compute a salt = bstr .cbor [
+     *     1. Compute the Diffie-Hellman shared secret: Z.
+     *     2. Compute a salt_input = bstr .cbor [
      *            source_version:    int,                    ; the protocol version used in `create`
      *            sink_pub_key:      bstr .cbor PlainPubKey, ; from input `peerPubKey`
      *            source_pub_key:    bstr .cbor PlainPubKey, ; from the output of `create`
@@ -143,7 +145,8 @@
      *            sink_cert_chain:   bstr .cbor ExplicitKeyDiceCertChain, ; from input `peerId`
      *            source_cert_chain: bstr .cbor ExplicitKeyDiceCertChain, ; from own identity
      *        ]
-     *     3. Extract a cryptographic secret S from Z, using the salt from #2 above.
+     *     3. Extract a cryptographic secret S from Z, using the SHA256 digest of the salt_input
+     *        as the salt.
      *     4. Derive two symmetric encryption keys of 256 bits with:
      *        i. b"KE_ENCRYPTION_KEY_SOURCE_TO_SINK" as context for the key used to encrypt outgoing
      *           messages
@@ -164,25 +167,26 @@
      *        part of the party's identity.
      *
      * @param peerPubKey - the public key of the key pair created by the peer (P2) for key exchange
+     *                     in `init`
      *
      * @param peerId - the persistent identity of the peer
      *
      * @param peerSignature - the signature created by the peer over the session id computed by the
-     *                        peer
+     *                        peer in `init`
      *
-     * @param peerNonce - nonce created by the peer
+     * @param peerNonce - nonce created by the peer in `init`
      *
      * @param peerVersion - an integer representing the protocol version (i.e. AIDL version)
      *                      negotiated with the peer
      *
-     * @param ownKey - the key created by P1 (source) in `create()` for key exchange
+     * @param ownKey - the key created by P1 (source) in `create` for key exchange
      *
-     * @return SessionInfo including the two shared key arcs from step #9, session id and the
-     * signature over the session id.
+     * @return SessionInfo including the two shared key arcs from step #9, session id from step #7
+     * and the signature over the session id from step #10.
      *
-     * Note: The two shared key arcs in the return type: `SessionInfo` serves two purposes:
+     * Note: The two shared key arcs in the return type: `SessionInfo` serve two purposes:
      * i. A mapping to correlate the key exchange protocol taken place with a particular peer and
-     *    subsequent AuthGraph protocols execued with the same peer.
+     *    subsequent AuthGraph protocols executed with the same peer.
      * ii.A way to minimize the in-memory storage for shared keys.
      * However, P1 should maintain some in-memory record to be able to verify that the shared key
      * arcs sent to any subsequent AuthGraph protocol methods are valid shared keys agreed with the
@@ -196,21 +200,33 @@
      * This method is invoked on P2 (sink).
      * Perform the following steps:
      *   0. If input `sharedKeys` is invalid (i.e. they cannot be decrypted with P2's per-boot key
-     *      or they are not in P2's in-memory records as valid shared keys agreed with the party
-     *      identified by `peerId`), return error: INVALID_SHARED_KEY_ARCS.
+     *      or they are not in P2's in-memory records for unfinished instances of a key exchange
+     *      protocol carried out with the party identified by the identity included in the
+     *      `source_id` protected header of the shared key arcs),
+     *      return error: INVALID_SHARED_KEY_ARCS.
      *   1. Verify that both shared key arcs have the same session id and peer identity.
-     *   2. Verify the peer's signature over the session id attached to the shared key arcs'
-     *      headers. If successful, proceed, otherwise, return error: INVALID_SIGNATURE.
-     *   3. Mark authentication_complete = true in the shared key arcs' headers
+     *   2. Verify the `peerSignature` over the session id included in the `session_id` protected
+     *      header of the shared key arcs.
+     *      If successful, proceed, otherwise, return error: INVALID_SIGNATURE.
+     *   3. Mark authentication_complete = true in the shared key arcs' headers.
      *
      * @param peerSignature - the signature created by the peer over the session id computed by the
-     *                        peer
+     *                        peer in `finish`
      *
      * @param sharedKeys - two shared key arcs created by P2 in `init`. P2 obtains from the arcs'
      *                     protected headers, the session id and the peer's identity to verify the
      *                     peer's signature over the session id.
      *
      * @return Arc[] - an array of two updated shared key arcs
+     *
+     * Note: The two returned shared key arcs serve two purposes:
+     * i. A mapping to correlate the key exchange protocol taken place with a particular peer and
+     *    subsequent AuthGraph protocols executed with the same peer.
+     * ii.A way to minimize the in-memory storage for shared keys.
+     * However, P2 should maintain some in-memory record to be able to verify that the shared key
+     * arcs sent to any subsequent AuthGraph protocol methods are valid shared keys agreed with the
+     * party identified by the identity included in the `source_id` protected header of the shared
+     * key arcs, to prevent any replay attacks.
      */
     Arc[2] authenticationComplete(in SessionIdSignature peerSignature, in Arc[2] sharedKeys);
 }
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/SessionInfo.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/SessionInfo.aidl
index ef49a1a..82b8c17 100644
--- a/security/authgraph/aidl/android/hardware/security/authgraph/SessionInfo.aidl
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/SessionInfo.aidl
@@ -26,8 +26,8 @@
 @RustDerive(Clone=true, Eq=true, PartialEq=true)
 parcelable SessionInfo {
     /**
-     * The arcs that encrypt the two derived symmetric encryption keys (for two-way communication)
-     * from the party's per-boot key.
+     * The arcs that encrypt the two derived symmetric encryption keys (for two-way communication).
+     * The encryption key is the party's per-boot key.
      */
     Arc[2] sharedKeys;
 
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/SessionInitiationInfo.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/SessionInitiationInfo.aidl
index c630d91..8179ac2 100644
--- a/security/authgraph/aidl/android/hardware/security/authgraph/SessionInitiationInfo.aidl
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/SessionInitiationInfo.aidl
@@ -27,20 +27,22 @@
 @RustDerive(Clone=true, Eq=true, PartialEq=true)
 parcelable SessionInitiationInfo {
     /**
-     * An ephemeral EC key created for the ECDH process.
+     * An ephemeral EC key created for the Elliptic-curve Diffie-Hellman (ECDH) process.
      */
     Key key;
 
     /**
-     * The identity of the party who created the Diffie-Hellman key exchange key.
+     * The identity of the party who creates this `SessionInitiationInfo`.
      */
     Identity identity;
 
     /**
-     * Nonce value specific to this session. The nonce serves three purposes:
+     * Nonce (a cryptographic random number of 16 bytes) specific to this session.
+     * The nonce serves three purposes:
      * 1. freshness of key exchange
      * 2. creating a session id (a publicly known value related to the exchanged keys)
-     * 3. usage as salt into the HKDF-EXTRACT function during key derivation from the shared DH key
+     * 3. usage as salt into the HKDF-EXTRACT function during key derivation from the Diffie-Hellman
+     *    shared secret
      */
     byte[] nonce;
 
diff --git a/security/authgraph/aidl/vts/functional/Android.bp b/security/authgraph/aidl/vts/functional/Android.bp
index 0e3480f..28a70e2 100644
--- a/security/authgraph/aidl/vts/functional/Android.bp
+++ b/security/authgraph/aidl/vts/functional/Android.bp
@@ -50,6 +50,7 @@
 rust_test {
     name: "VtsAidlAuthGraphRoleTest",
     srcs: ["role_test.rs"],
+    require_root: true,
     test_suites: [
         "general-tests",
         "vts",
diff --git a/security/authgraph/aidl/vts/functional/role_test.rs b/security/authgraph/aidl/vts/functional/role_test.rs
index 71a2fae..3075d8a 100644
--- a/security/authgraph/aidl/vts/functional/role_test.rs
+++ b/security/authgraph/aidl/vts/functional/role_test.rs
@@ -22,13 +22,18 @@
 use android_hardware_security_authgraph::aidl::android::hardware::security::authgraph::{
     IAuthGraphKeyExchange::IAuthGraphKeyExchange,
 };
+use binder::StatusCode;
 
 const AUTH_GRAPH_NONSECURE: &str =
     "android.hardware.security.authgraph.IAuthGraphKeyExchange/nonsecure";
 
 /// Retrieve the /nonsecure instance of AuthGraph, which supports both sink and source roles.
 fn get_nonsecure() -> Option<binder::Strong<dyn IAuthGraphKeyExchange>> {
-    binder::get_interface(AUTH_GRAPH_NONSECURE).ok()
+    match binder::get_interface(AUTH_GRAPH_NONSECURE) {
+        Ok(ag) => Some(ag),
+        Err(StatusCode::NAME_NOT_FOUND) => None,
+        Err(e) => panic!("failed to get AuthGraph/nonsecure: {e:?}"),
+    }
 }
 
 /// Macro to require availability of a /nonsecure instance of AuthGraph.
diff --git a/security/authgraph/aidl/vts/functional/source.rs b/security/authgraph/aidl/vts/functional/source.rs
index 4178a99..a1e76b3 100644
--- a/security/authgraph/aidl/vts/functional/source.rs
+++ b/security/authgraph/aidl/vts/functional/source.rs
@@ -250,9 +250,13 @@
         &corrupt_key,
     );
 
-    let err = result.expect_err("expect failure with corrupt signature");
-    assert_eq!(
-        err,
-        binder::Status::new_service_specific_error(Error::INVALID_PRIV_KEY_ARC_IN_KEY.0, None)
+    let err = result.expect_err("expect failure with corrupt key");
+    assert!(
+        err == binder::Status::new_service_specific_error(Error::INVALID_KE_KEY.0, None)
+            || err
+                == binder::Status::new_service_specific_error(
+                    Error::INVALID_PRIV_KEY_ARC_IN_KEY.0,
+                    None
+                )
     );
 }
diff --git a/security/authgraph/default/Android.bp b/security/authgraph/default/Android.bp
index c481075..7894477 100644
--- a/security/authgraph/default/Android.bp
+++ b/security/authgraph/default/Android.bp
@@ -46,11 +46,11 @@
     name: "android.hardware.security.authgraph-service.nonsecure",
     relative_install_path: "hw",
     vendor: true,
-    init_rc: ["authgraph.rc"],
-    vintf_fragments: ["authgraph.xml"],
+    installable: false, // install com.android.hardware.security.authgraph
     defaults: [
         "authgraph_use_latest_hal_aidl_rust",
     ],
+    prefer_rlib: true,
     rustlibs: [
         "libandroid_logger",
         "libauthgraph_hal",
@@ -80,3 +80,34 @@
         ],
     },
 }
+
+prebuilt_etc {
+    name: "authgraph.xml",
+    src: "authgraph.xml",
+    sub_dir: "vintf",
+    installable: false,
+}
+
+prebuilt_etc {
+    name: "authgraph.rc",
+    src: "authgraph.rc",
+    installable: false,
+}
+
+apex {
+    name: "com.android.hardware.security.authgraph",
+    manifest: "apex_manifest.json",
+    file_contexts: "apex_file_contexts",
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
+    vendor: true,
+    updatable: false,
+
+    binaries: [
+        "android.hardware.security.authgraph-service.nonsecure",
+    ],
+    prebuilts: [
+        "authgraph.rc",
+        "authgraph.xml",
+    ],
+}
diff --git a/security/authgraph/default/apex_file_contexts b/security/authgraph/default/apex_file_contexts
new file mode 100644
index 0000000..9a54613
--- /dev/null
+++ b/security/authgraph/default/apex_file_contexts
@@ -0,0 +1,3 @@
+(/.*)?                                          u:object_r:vendor_file:s0
+/etc(/.*)?                                      u:object_r:vendor_configs_file:s0
+/bin/hw/android\.hardware\.security\.authgraph-service\.nonsecure  u:object_r:hal_authgraph_default_exec:s0
diff --git a/security/authgraph/default/apex_manifest.json b/security/authgraph/default/apex_manifest.json
new file mode 100644
index 0000000..0723846
--- /dev/null
+++ b/security/authgraph/default/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+    "name": "com.android.hardware.security.authgraph",
+    "version": 1
+}
\ No newline at end of file
diff --git a/security/authgraph/default/authgraph.rc b/security/authgraph/default/authgraph.rc
index 0222994..2d07542 100644
--- a/security/authgraph/default/authgraph.rc
+++ b/security/authgraph/default/authgraph.rc
@@ -1,4 +1,4 @@
-service vendor.authgraph /vendor/bin/hw/android.hardware.security.authgraph-service.nonsecure
+service vendor.authgraph /apex/com.android.hardware.security.authgraph/bin/hw/android.hardware.security.authgraph-service.nonsecure
     interface aidl android.hardware.security.authgraph.IAuthGraph/nonsecure
     class hal
     user nobody
diff --git a/security/authgraph/default/src/lib.rs b/security/authgraph/default/src/lib.rs
index 43d037c..14741aa 100644
--- a/security/authgraph/default/src/lib.rs
+++ b/security/authgraph/default/src/lib.rs
@@ -18,38 +18,11 @@
 
 use authgraph_boringssl as boring;
 use authgraph_core::{
-    error,
-    key::MillisecondsSinceEpoch,
-    keyexchange,
+    error, keyexchange,
     ta::{AuthGraphTa, Role},
-    traits,
 };
 use authgraph_hal::channel::SerializedChannel;
 use std::sync::{Arc, Mutex};
-use std::time::Instant;
-
-/// Monotonic clock with an epoch that starts at the point of construction.
-/// (This makes it unsuitable for use outside of testing, because the epoch
-/// will not match that of any other component.)
-pub struct StdClock(Instant);
-
-impl Default for StdClock {
-    fn default() -> Self {
-        Self(Instant::now())
-    }
-}
-
-impl traits::MonotonicClock for StdClock {
-    fn now(&self) -> MillisecondsSinceEpoch {
-        let millis: i64 = self
-            .0
-            .elapsed()
-            .as_millis()
-            .try_into()
-            .expect("failed to fit timestamp in i64");
-        MillisecondsSinceEpoch(millis)
-    }
-}
 
 /// Implementation of the AuthGraph TA that runs locally in-process (and which is therefore
 /// insecure).
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
index aa7bf28..be29f59 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -974,8 +974,8 @@
      * time in milliseconds.  This value is used when generating attestation or self signed
      * certificates.  ErrorCode::MISSING_NOT_BEFORE must be returned if this tag is not provided if
      * this tag is not provided to generateKey or importKey.  For importWrappedKey, there is no way
-     * to specify the value of this tag for the wrapped key, so a value of 0 must be used for
-     * certificate generation.
+     * to specify the value of this tag for a wrapped asymmetric key, so a value of 0 is suggested
+     * for certificate generation.
      */
     CERTIFICATE_NOT_BEFORE = TagType.DATE | 1008,
 
@@ -983,8 +983,9 @@
      * Tag::CERTIFICATE_NOT_AFTER the end of the validity of the certificate in UNIX epoch time in
      * milliseconds.  This value is used when generating attestation or self signed certificates.
      * ErrorCode::MISSING_NOT_AFTER must be returned if this tag is not provided to generateKey or
-     * importKey.  For importWrappedKey, there is no way to specify the value of this tag for the
-     * wrapped key, so a value of 253402300799000 is used for certificate generation.
+     * importKey.  For importWrappedKey, there is no way to specify the value of this tag for a
+     * wrapped asymmetric key, so a value of 253402300799000 is suggested for certificate
+     * generation.
      */
     CERTIFICATE_NOT_AFTER = TagType.DATE | 1009,
 
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 822770d..d3f6ae3 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -1792,6 +1792,12 @@
     std::string empty_boot_key(32, '\0');
     std::string verified_boot_key_str((const char*)verified_boot_key.data(),
                                       verified_boot_key.size());
+    if (get_vsr_api_level() >= __ANDROID_API_V__) {
+        // The attestation should contain the SHA-256 hash of the verified boot
+        // key.  However, this was not checked for earlier versions of the KeyMint
+        // HAL so only be strict for VSR-V and above.
+        EXPECT_LE(verified_boot_key.size(), 32);
+    }
     EXPECT_NE(property_get("ro.boot.verifiedbootstate", property_value, ""), 0);
     if (!strcmp(property_value, "green")) {
         EXPECT_EQ(verified_boot_state, VerifiedBoot::VERIFIED);
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index a8f17dd..d4adab5 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -4123,13 +4123,13 @@
  * when the EC_CURVE is not explicitly specified.
  */
 TEST_P(ImportKeyTest, EcdsaSuccessCurveNotSpecified) {
-    if (AidlVersion() < 4) {
+    if (get_vsr_api_level() < __ANDROID_API_V__) {
         /*
-         * The KeyMint spec before V4 was not clear as to whether EC_CURVE was optional on import of
-         * EC keys. However, this was not checked at the time so we can only be strict about
-         * checking this for implementations of KeyMint version 4 and above.
+         * The KeyMint spec was previously not clear as to whether EC_CURVE was optional on import
+         * of EC keys. However, this was not checked at the time so we can only be strict about
+         * checking this for implementations at VSR-V or later.
          */
-        GTEST_SKIP() << "Skipping EC_CURVE on import only strict since KeyMint v4";
+        GTEST_SKIP() << "Skipping EC_CURVE on import only strict >= VSR-V";
     }
 
     ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
diff --git a/security/keymint/aidl/vts/functional/SecureElementProvisioningTest.cpp b/security/keymint/aidl/vts/functional/SecureElementProvisioningTest.cpp
index c9a156d..9f7322a 100644
--- a/security/keymint/aidl/vts/functional/SecureElementProvisioningTest.cpp
+++ b/security/keymint/aidl/vts/functional/SecureElementProvisioningTest.cpp
@@ -114,6 +114,12 @@
         const auto& vbKey = rot->asArray()->get(pos++);
         ASSERT_TRUE(vbKey);
         ASSERT_TRUE(vbKey->asBstr());
+        if (get_vsr_api_level() >= __ANDROID_API_V__) {
+            // The attestation should contain the SHA-256 hash of the verified boot
+            // key.  However, this not was checked for earlier versions of the KeyMint
+            // HAL so only be strict for VSR-V and above.
+            ASSERT_LE(vbKey->asBstr()->value().size(), 32);
+        }
 
         const auto& deviceLocked = rot->asArray()->get(pos++);
         ASSERT_TRUE(deviceLocked);
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
index 34f7ce4..6edbfc1 100644
--- a/security/keymint/support/remote_prov_utils.cpp
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -520,6 +520,15 @@
                    std::to_string(info.versionNumber) + ").";
         }
     }
+    // Bypasses the device info validation since the device info in AVF is currently
+    // empty. Check b/299256925 for more information.
+    //
+    // TODO(b/300911665): This check is temporary and will be replaced once the markers
+    // on the DICE chain become available. We need to determine if the CSR is from the
+    // RKP VM using the markers on the DICE chain.
+    if (info.uniqueId == "AVF Remote Provisioning 1") {
+        return std::move(parsed);
+    }
 
     std::string error;
     std::string tmp;
diff --git a/security/rkp/README.md b/security/rkp/README.md
index 15ea817..71f70cb 100644
--- a/security/rkp/README.md
+++ b/security/rkp/README.md
@@ -190,3 +190,30 @@
 *   [RpcHardwareInfo](https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl)
 *   [DeviceInfo](https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/DeviceInfo.aidl)
 
+### Support for Android Virtualization Framework
+
+The Android Virtualization Framwork (AVF) relies on RKP to provision keys for VMs. A
+privileged vm, the RKP VM, is reponsible for generating and managing the keys for client
+VMs that run virtualized workloads. See the following for more background information on the
+RKP VM:
+*    [rkp-vm]: https://android.googlesource.com/platform/packages/modules/Virtualization/+/main/service_vm/README.md#rkp-vm-remote-key-provisioning-virtual-machine
+*    [rkp-service]: https://source.android.com/docs/core/ota/modular-system/remote-key-provisioning#stack-architecture
+
+It is important to distinquish the RKP VM from other components, such as KeyMint. An
+[RKP VM marker](https://pigweed.googlesource.com/open-dice/+/HEAD/docs/android.md#configuration-descriptor)
+(key `-70006) is used for this purpose. The existence or absence of this marker is used to
+identify the type of component decribed by a given DICE chain.
+
+The following describes which certificate types may be request based on the RKP VM marker:
+1. "rkp-vm": If a DICE chain has zero or more certificates without the RKP VM
+   marker followed by one or more certificates with the marker, then that chain
+   describes an RKP VM. If there are further certificates without the RKP VM
+   marker, then the chain does not describe an RKP VM.
+
+   Implementations must include the first RPK VM marker as early as possible
+   after the point of divergence between TEE and non-TEE components in the DICE
+   chain, prior to loading the Android Bootloader (ABL).
+2. "widevine" or "keymint": If there are no certificates with the RKP VM
+   marker then it describes a TEE component.
+3. None: Any component described by a DICE chain that does not match the above
+   two categories.
\ No newline at end of file
diff --git a/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl b/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl
index 61404d4..3c43238 100644
--- a/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl
+++ b/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl
@@ -14,8 +14,9 @@
 ; be extended without requiring a version bump of the HAL. Custom certificate types may
 ; be used, but the provisioning server may reject the request for an unknown certificate
 ; type. The currently defined certificate types are:
-;  - "widevine"
-;  - "keymint"
+;  * "widevine" -- Widevine content protection system
+;  * "keymint"  -- KeyMint HAL
+;  * "rkp-vm"   -- See "Support for Android Virtualization Framework" in the README.md file.
 CertificateType = tstr
 
 KeysToSign = [ * PublicKey ]   ; Please see PublicKey.cddl for the PublicKey definition.
@@ -112,6 +113,7 @@
     ? -70003 : int / tstr,                   ; Component version
     ? -70004 : null,                         ; Resettable
     ? -70005 : uint,                         ; Security version
+    ? -70006 : null,                         ; RKP VM marker
 }
 
 ; Each entry in the DICE chain is a DiceChainEntryPayload signed by the key from the previous
diff --git a/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index 62463eb..a1de93e 100644
--- a/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -55,6 +55,8 @@
 
 constexpr uint8_t MIN_CHALLENGE_SIZE = 0;
 constexpr uint8_t MAX_CHALLENGE_SIZE = 64;
+const string RKP_VM_INSTANCE_NAME =
+        "android.hardware.security.keymint.IRemotelyProvisionedComponent/avf";
 
 #define INSTANTIATE_REM_PROV_AIDL_TEST(name)                                         \
     GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name);                             \
@@ -181,7 +183,12 @@
             provisionable_ = IRemotelyProvisionedComponent::fromBinder(binder);
         }
         ASSERT_NE(provisionable_, nullptr);
-        ASSERT_TRUE(provisionable_->getHardwareInfo(&rpcHardwareInfo).isOk());
+        auto status = provisionable_->getHardwareInfo(&rpcHardwareInfo);
+        if (GetParam() == RKP_VM_INSTANCE_NAME &&
+            status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+            GTEST_SKIP() << "The RKP VM is not supported on this system.";
+        }
+        ASSERT_TRUE(status.isOk());
     }
 
     static vector<string> build_params() {
@@ -207,7 +214,11 @@
         ASSERT_NE(rpc, nullptr);
 
         RpcHardwareInfo hwInfo;
-        ASSERT_TRUE(rpc->getHardwareInfo(&hwInfo).isOk());
+        auto status = rpc->getHardwareInfo(&hwInfo);
+        if (hal == RKP_VM_INSTANCE_NAME && status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+            GTEST_SKIP() << "The RKP VM is not supported on this system.";
+        }
+        ASSERT_TRUE(status.isOk());
 
         if (hwInfo.versionNumber >= VERSION_WITH_UNIQUE_ID_SUPPORT) {
             ASSERT_TRUE(hwInfo.uniqueId);
diff --git a/security/secretkeeper/aidl/Android.bp b/security/secretkeeper/aidl/Android.bp
new file mode 100644
index 0000000..c77d299
--- /dev/null
+++ b/security/secretkeeper/aidl/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aidl_interface {
+    name: "android.hardware.security.secretkeeper",
+    vendor_available: true,
+    srcs: ["android/hardware/security/secretkeeper/*.aidl"],
+    stability: "vintf",
+    backend: {
+        ndk: {
+            enabled: true,
+        },
+        rust: {
+            enabled: true,
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.virt",
+            ],
+        },
+    },
+}
diff --git a/security/secretkeeper/aidl/aidl_api/android.hardware.security.secretkeeper/current/android/hardware/security/secretkeeper/ISecretkeeper.aidl b/security/secretkeeper/aidl/aidl_api/android.hardware.security.secretkeeper/current/android/hardware/security/secretkeeper/ISecretkeeper.aidl
new file mode 100644
index 0000000..2eb33c5
--- /dev/null
+++ b/security/secretkeeper/aidl/aidl_api/android.hardware.security.secretkeeper/current/android/hardware/security/secretkeeper/ISecretkeeper.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 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.secretkeeper;
+@VintfStability
+interface ISecretkeeper {
+  byte[] processSecretManagementRequest(in byte[] request);
+}
diff --git a/security/secretkeeper/aidl/android/hardware/security/secretkeeper/ISecretkeeper.aidl b/security/secretkeeper/aidl/android/hardware/security/secretkeeper/ISecretkeeper.aidl
new file mode 100644
index 0000000..af715a9
--- /dev/null
+++ b/security/secretkeeper/aidl/android/hardware/security/secretkeeper/ISecretkeeper.aidl
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2023 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.secretkeeper;
+
+@VintfStability
+/**
+ * Secretkeeper service definition.
+ *
+ * An ISecretkeeper instance provides secure storage of secrets on behalf of other components in
+ * Android, in particular for protected virtual machine instances. From the perspective of security
+ * privilege, Secretkeeper must be implemented in an environment with privilege higher than any of
+ * its clients. Since AVF based protected Virtual Machines are one set of its clients, the
+ * implementation of ISecretkeeper should live in a secure environment, such as:
+ * - A trusted execution environment such as ARM TrustZone.
+ * - A completely separate, purpose-built and certified secure CPU.
+ *
+ * TODO(b/291224769): Extend the HAL interface to include:
+ * 1. Session setup api: This is used to perform cryptographic operations that allow shared keys to
+ * be exchanged between session participants, typically (but not necessarily) a pVM instance and
+ * Secretkeeper. This session setup is based on public key cryptography.
+ * 2. Dice policy operation - These allow sealing of the secrets with a class of Dice chains.
+ * Typical operations are (securely) updating the dice policy sealing the Secrets above. These
+ * operations are core to AntiRollback protected secrets - ie, ensuring secrets of a pVM are only
+ * accessible to same or higher versions of the images.
+ * 3. Maintenance api: This is required for removing the Secretkeeper entries for obsolete pvMs.
+ */
+interface ISecretkeeper {
+    /**
+     * processSecretManagementRequest method is used for interacting with the Secret Management API
+     *
+     * Secret Management API: The clients can use this API to store (& get) 32 bytes of data.
+     * The API is a CBOR based protocol, which follows request/response model.
+     * See SecretManagement.cddl for the API spec.
+     *
+     * Further, the requests (from client) & responses (from service) must be encrypted into
+     * ProtectedRequestPacket & ProtectedResponsePacket using symmetric keys agreed between
+     * the client & service. This cryptographic protection is required because the messages are
+     * ferried via Android, which is allowed to be outside the TCB of clients (for example protected
+     * Virtual Machines). For this, service (& client) must implement a key exchange protocol, which
+     * is critical for establishing the secure channel.
+     *
+     * Secretkeeper database should guarantee the following properties:
+     * 1. Confidentiality: No entity (of security privilege lower than Secretkeeper) should
+     *    be able to get a client's data in clear.
+     *
+     * 2. Integrity: The data is protected against malicious Android OS tampering with database.
+     *    ie, if Android (userspace & kernel) tampers with the client's secret, the Secretkeeper
+     *    service must be able to detect it & return error when clients requests for their secrets.
+     *    Note: the integrity requirements also include Antirollback protection ie, reverting the
+     *    database into an old state should be detected.
+     *
+     * 3. The data is persistent across device boot.
+     *    Note: Denial of service is not in scope. A malicious Android may be able to delete data,
+     *    but for ideal Android, the data should be persistent.
+     *
+     * @param CBOR-encoded ProtectedRequestPacket. See SecretManagement.cddl for its definition.
+     * @return CBOR-encoded ProtectedResponsePacket. See SecretManagement.cddl for its definition
+     */
+    byte[] processSecretManagementRequest(in byte[] request);
+}
diff --git a/security/secretkeeper/aidl/android/hardware/security/secretkeeper/SecretManagement.cddl b/security/secretkeeper/aidl/android/hardware/security/secretkeeper/SecretManagement.cddl
new file mode 100644
index 0000000..5631937
--- /dev/null
+++ b/security/secretkeeper/aidl/android/hardware/security/secretkeeper/SecretManagement.cddl
@@ -0,0 +1,116 @@
+; CDDL for the Secret Management API.
+; Also see processSecretManagementRequest method in ISecretkeeper.aidl
+
+; ProtectedRequestPacket is used by client for accessing Secret Management API
+; in Secretkeeper service. The service returns ProtectedResponsePacket of the corresponding type.
+
+; ProtectedRequestPacket & ProtectedResponsePacket are encrypted wrappers
+; on RequestPacket & ResponsePacket using symmetric keys agreed between Secretkeeper & clients
+; (these are referred to as KeySourceToSink & KeySinkToSource)
+;
+; The API operation required is encoded using 'Opcode', the arguments using 'Params'
+; and returned values as 'Result'.
+
+ProtectedRequestPacket =
+        ProtectedGetVersionRequest / ProtectedStoreSecretRequest / ProtectedGetSecretRequest
+ProtectedResponsePacket =
+        ProtectedGetVersionResponse / ProtectedStoreSecretResponse / ProtectedGetSecretResponse
+
+ProtectedGetVersionRequest = ProtectedRequestPacket<GetVersionRequestPacket>
+ProtectedGetVersionResponse = ProtectedResponsePacket<GetVersionResponsePacket>
+ProtectedStoreSecretRequest = ProtectedRequestPacket<StoreSecretRequestPacket>
+ProtectedStoreSecretResponse = ProtectedResponsePacket<StoreSecretResponsePacket>
+ProtectedGetSecretRequest = ProtectedRequestPacket<GetSecretRequestPacket>
+ProtectedGetSecretResponse = ProtectedResponsePacket<GetSecretResponsePacket>
+
+GetVersionRequestPacket = RequestPacket<GetVersionOpcode, GetVersionParams>
+GetVersionResponsePacket = ResponsePacket<GetVersionResult>
+StoreSecretRequestPacket = RequestPacket<StoreSecretOpcode, StoreSecretParams>
+StoreSecretResponsePacket = ResponsePacket<StoreSecretResult>
+GetSecretRequestPacket = RequestPacket<GetOpcode, GetSecretParams>
+GetSecretResponsePacket = ResponsePacket<GetSecretResult>
+
+RequestPacket<Opcode, Params> = [
+    Opcode,
+    Params
+]
+ResponsePacket<Result> = ResponsePacketError / ResponsePacketSuccess<Result>
+
+ResponsePacketSuccess = [
+    0,                          ; Indicates successful Response
+    result : Result
+]
+ResponsePacketError = [
+    error_code: ErrorCode,      ; Indicate the error
+    error_message: tstr         ; Additional human-readable context
+]
+
+Opcode = &(
+    GetVersionOpcode: 1,     ; Get version of the SecretManagement API
+    StoreSecretOpcode: 2,          ; Store a secret
+    GetSecretOpcode: 3,            ; Get the secret
+)
+
+GetVersionParams = ()
+GetVersionResult = (version : uint)
+
+StoreSecretParams = (
+    id : bstr .size 64              ; Unique identifier of the secret
+    secret : bstr .size 32,
+    sealing_policy : bstr .cbor DicePolicy,    ; See DicePolicy.cddl for definition of DicePolicy
+)
+StoreSecretResult = ()
+
+GetSecretParams = (
+    id : bstr .size 64              ; Unique identifier of the secret
+    ; Use this to update the sealing policy associated with a secret during GetSecret operation.
+    updated_sealing_policy : bstr .cbor DicePolicy / nil,
+)
+GetSecretResult = (secret : bstr .size 32)
+
+
+ProtectedRequestPacket<Payload, Key> = CryptoPayload<Payload, KeySourceToSink>
+ProtectedResponsePacket<Payload, Key> = ProtectedResponseError
+                                    / ProtectedResponseSuccess<Payload>
+
+ProtectedResponseSuccess<Payload> = [
+    0,                                ; Indicates successful crypto operations. Note: Payload
+                                                    ; may contain Error from functional layer.
+    message: CryptoPayload<Payload, KeySinkToSource>         ; message is the encrypted payload
+]
+
+ProtectedResponseError = [
+    error_code: CryptoErrorCode,           ; Indicates the error. This is in cleartext & will be
+                                           ; visible to Android. These are errors from crypto
+                                           ; layer & indicates the request could not even be read
+    message: tstr                          ; Additional human-readable context
+]
+
+CryptoPayload<Payload, Key> = [         ; COSE_Encrypt0 (untagged), [RFC 9052 s5.2]
+    protected: bstr .cbor {
+        1 : 3,                  ; Algorithm: AES-GCM mode w/ 256-bit key, 128-bit tag
+        4 : bstr                ; key identifier, uniquely identifies the session
+                                ; TODO(b/291228560): Refer to the Key Exchange spec.
+    },
+    unprotected: {
+        5 : bstr .size 12          ; IV
+    },
+    ciphertext : bstr     ; AES-GCM-256(Key, bstr .cbor Payload)
+                          ; AAD for the encryption is CBOR-serialized
+                          ; Enc_structure (RFC 9052 s5.3) with empty external_aad.
+]
+
+; TODO(b/291224769): Create a more exhaustive set of CryptoErrorCode
+CryptoErrorCode = &(
+    CryptoErrorCode_SessionExpired: 1,
+)
+
+; TODO(b/291224769): Create a more exhaustive set of ErrorCodes
+ErrorCode = &(
+    ; Use this as if no other error code can be used.
+    ErrorCode_UnexpectedServerError: 1,
+    ; Indicate the Request was malformed & hence couldnt be served.
+    ErrorCode_RequestMalformed: 2,
+)
+
+; INCLUDE DicePolicy.cddl for: DicePolicy
\ No newline at end of file
diff --git a/security/secretkeeper/aidl/vts/Android.bp b/security/secretkeeper/aidl/vts/Android.bp
new file mode 100644
index 0000000..6818298
--- /dev/null
+++ b/security/secretkeeper/aidl/vts/Android.bp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_test {
+    name: "VtsSecretkeeperTargetTest",
+    srcs: ["secretkeeper_test_client.rs"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+    rustlibs: [
+        "libsecretkeeper_comm_nostd",
+        "android.hardware.security.secretkeeper-V1-rust",
+        "libbinder_rs",
+        "liblog_rust",
+    ],
+    require_root: true,
+}
diff --git a/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs b/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs
new file mode 100644
index 0000000..28923f7
--- /dev/null
+++ b/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#[cfg(test)]
+use binder::StatusCode;
+use log::warn;
+use secretkeeper_comm::data_types::error::SecretkeeperError;
+use secretkeeper_comm::data_types::request::Request;
+use secretkeeper_comm::data_types::request_response_impl::{
+    GetVersionRequest, GetVersionResponse,
+};
+use secretkeeper_comm::data_types::response::Response;
+use secretkeeper_comm::data_types::packet::{ResponsePacket, ResponseType};
+use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::ISecretkeeper::ISecretkeeper;
+
+const SECRETKEEPER_IDENTIFIER: &str =
+    "android.hardware.security.secretkeeper.ISecretkeeper/nonsecure";
+const CURRENT_VERSION: u64 = 1;
+
+fn get_connection() -> Option<binder::Strong<dyn ISecretkeeper>> {
+    match binder::get_interface(SECRETKEEPER_IDENTIFIER) {
+        Ok(sk) => Some(sk),
+        Err(StatusCode::NAME_NOT_FOUND) => None,
+        Err(e) => {
+            panic!(
+                "unexpected error while fetching connection to Secretkeeper {:?}",
+                e
+            );
+        }
+    }
+}
+
+// TODO(b/2797757): Add tests that match different HAL defined objects (like request/response)
+// with expected bytes.
+
+#[test]
+fn secret_management_get_version() {
+    let secretkeeper = match get_connection() {
+        Some(sk) => sk,
+        None => {
+            warn!("Secretkeeper HAL is unavailable, skipping test");
+            return;
+        }
+    };
+    let request = GetVersionRequest {};
+    let request_packet = request.serialize_to_packet();
+    let request_bytes = request_packet.into_bytes().unwrap();
+
+    // TODO(b/291224769) The request will need to be encrypted & response need to be decrypted
+    // with key & related artifacts pre-shared via Authgraph Key Exchange HAL.
+
+    let response_bytes = secretkeeper
+        .processSecretManagementRequest(&request_bytes)
+        .unwrap();
+
+    let response_packet = ResponsePacket::from_bytes(&response_bytes).unwrap();
+    assert_eq!(
+        response_packet.response_type().unwrap(),
+        ResponseType::Success
+    );
+    let get_version_response =
+        *GetVersionResponse::deserialize_from_packet(response_packet).unwrap();
+    assert_eq!(get_version_response.version(), CURRENT_VERSION);
+}
+
+#[test]
+fn secret_management_malformed_request() {
+    let secretkeeper = match get_connection() {
+        Some(sk) => sk,
+        None => {
+            warn!("Secretkeeper HAL is unavailable, skipping test");
+            return;
+        }
+    };
+    let request = GetVersionRequest {};
+    let request_packet = request.serialize_to_packet();
+    let mut request_bytes = request_packet.into_bytes().unwrap();
+
+    // Deform the request
+    request_bytes[0] = !request_bytes[0];
+
+    // TODO(b/291224769) The request will need to be encrypted & response need to be decrypted
+    // with key & related artifacts pre-shared via Authgraph Key Exchange HAL.
+
+    let response_bytes = secretkeeper
+        .processSecretManagementRequest(&request_bytes)
+        .unwrap();
+
+    let response_packet = ResponsePacket::from_bytes(&response_bytes).unwrap();
+    assert_eq!(
+        response_packet.response_type().unwrap(),
+        ResponseType::Error
+    );
+    let err = *SecretkeeperError::deserialize_from_packet(response_packet).unwrap();
+    assert_eq!(err, SecretkeeperError::RequestMalformed);
+}
diff --git a/security/secretkeeper/default/Android.bp b/security/secretkeeper/default/Android.bp
new file mode 100644
index 0000000..1c39fa6
--- /dev/null
+++ b/security/secretkeeper/default/Android.bp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_binary {
+    name: "android.hardware.security.secretkeeper-service.nonsecure",
+    relative_install_path: "hw",
+    vendor: true,
+    init_rc: ["secretkeeper.rc"],
+    vintf_fragments: ["secretkeeper.xml"],
+    rustlibs: [
+        "android.hardware.security.secretkeeper-V1-rust",
+        "libandroid_logger",
+        "libbinder_rs",
+        "liblog_rust",
+        "libsecretkeeper_comm_nostd",
+    ],
+    srcs: [
+        "src/main.rs",
+    ],
+}
diff --git a/security/secretkeeper/default/secretkeeper.rc b/security/secretkeeper/default/secretkeeper.rc
new file mode 100644
index 0000000..f39f9b7
--- /dev/null
+++ b/security/secretkeeper/default/secretkeeper.rc
@@ -0,0 +1,5 @@
+service vendor.secretkeeper /vendor/bin/hw/android.hardware.security.secretkeeper-service.nonsecure
+    interface aidl android.hardware.security.secretkeeper.ISecretkeeper/nonsecure
+    class hal
+    user nobody
+    group nobody
diff --git a/security/secretkeeper/default/secretkeeper.xml b/security/secretkeeper/default/secretkeeper.xml
new file mode 100644
index 0000000..40aebe0
--- /dev/null
+++ b/security/secretkeeper/default/secretkeeper.xml
@@ -0,0 +1,28 @@
+<manifest version="1.0" type="device">
+<!--
+/*
+** Copyright 2022, The Android Open Source Project.
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+    <hal format="aidl">
+        <name>android.hardware.security.secretkeeper</name>
+        <version>1</version>
+        <interface>
+            <name>ISecretkeeper</name>
+            <instance>nonsecure</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/security/secretkeeper/default/src/main.rs b/security/secretkeeper/default/src/main.rs
new file mode 100644
index 0000000..2d367c5
--- /dev/null
+++ b/security/secretkeeper/default/src/main.rs
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+use binder::{BinderFeatures, Interface};
+use log::{error, info, Level};
+use secretkeeper_comm::data_types::error::SecretkeeperError;
+use secretkeeper_comm::data_types::packet::{RequestPacket, ResponsePacket};
+use secretkeeper_comm::data_types::request::Request;
+use secretkeeper_comm::data_types::request_response_impl::{
+    GetVersionRequest, GetVersionResponse, Opcode,
+};
+use secretkeeper_comm::data_types::response::Response;
+
+use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::ISecretkeeper::{
+    BnSecretkeeper, BpSecretkeeper, ISecretkeeper,
+};
+
+const CURRENT_VERSION: u64 = 1;
+
+#[derive(Debug, Default)]
+pub struct NonSecureSecretkeeper;
+
+impl Interface for NonSecureSecretkeeper {}
+
+impl ISecretkeeper for NonSecureSecretkeeper {
+    fn processSecretManagementRequest(&self, request: &[u8]) -> binder::Result<Vec<u8>> {
+        Ok(self.process_opaque_request(request))
+    }
+}
+
+impl NonSecureSecretkeeper {
+    // A set of requests to Secretkeeper are 'opaque' - encrypted bytes with inner structure
+    // described by CDDL. They need to be decrypted, deserialized and processed accordingly.
+    fn process_opaque_request(&self, request: &[u8]) -> Vec<u8> {
+        // TODO(b/291224769) The request will need to be decrypted & response need to be encrypted
+        // with key & related artifacts pre-shared via Authgraph Key Exchange HAL.
+        self.process_opaque_request_unhandled_error(request)
+            .unwrap_or_else(
+                // SecretkeeperError is also a valid 'Response', serialize to a response packet.
+                |sk_err| {
+                    Response::serialize_to_packet(&sk_err)
+                        .into_bytes()
+                        .expect("Panicking due to serialization failing")
+                },
+            )
+    }
+
+    fn process_opaque_request_unhandled_error(
+        &self,
+        request: &[u8],
+    ) -> Result<Vec<u8>, SecretkeeperError> {
+        let request_packet = RequestPacket::from_bytes(request).map_err(|e| {
+            error!("Failed to get Request packet from bytes: {:?}", e);
+            SecretkeeperError::RequestMalformed
+        })?;
+        let response_packet = match request_packet
+            .opcode()
+            .map_err(|_| SecretkeeperError::RequestMalformed)?
+        {
+            Opcode::GetVersion => Self::process_get_version_request(request_packet)?,
+            _ => todo!("TODO(b/291224769): Unimplemented operations"),
+        };
+
+        response_packet
+            .into_bytes()
+            .map_err(|_| SecretkeeperError::UnexpectedServerError)
+    }
+
+    fn process_get_version_request(
+        request: RequestPacket,
+    ) -> Result<ResponsePacket, SecretkeeperError> {
+        // Deserialization really just verifies the structural integrity of the request such
+        // as args being empty.
+        let _request = GetVersionRequest::deserialize_from_packet(request)
+            .map_err(|_| SecretkeeperError::RequestMalformed)?;
+        let response = GetVersionResponse::new(CURRENT_VERSION);
+        Ok(response.serialize_to_packet())
+    }
+}
+
+fn main() {
+    // Initialize Android logging.
+    android_logger::init_once(
+        android_logger::Config::default()
+            .with_tag("NonSecureSecretkeeper")
+            .with_min_level(Level::Info)
+            .with_log_id(android_logger::LogId::System),
+    );
+    // Redirect panic messages to logcat.
+    std::panic::set_hook(Box::new(|panic_info| {
+        error!("{}", panic_info);
+    }));
+
+    let service = NonSecureSecretkeeper::default();
+    let service_binder = BnSecretkeeper::new_binder(service, BinderFeatures::default());
+    let service_name = format!(
+        "{}/nonsecure",
+        <BpSecretkeeper as ISecretkeeper>::get_descriptor()
+    );
+    binder::add_service(&service_name, service_binder.as_binder()).unwrap_or_else(|e| {
+        panic!(
+            "Failed to register service {} because of {:?}.",
+            service_name, e
+        );
+    });
+    info!("Registered Binder service, joining threadpool.");
+    binder::ProcessState::join_thread_pool();
+}
diff --git a/threadnetwork/aidl/Android.bp b/threadnetwork/aidl/Android.bp
index c621b81..7e674e0 100644
--- a/threadnetwork/aidl/Android.bp
+++ b/threadnetwork/aidl/Android.bp
@@ -15,9 +15,6 @@
             apex_available: [
                 "//apex_available:platform",
                 "com.android.tethering",
-                // Keep the threadnetwork apex to make it buildable on udc-mainline-prod.
-                // TODO: remove it after moving ot-daemon into tethering.
-                "com.android.threadnetwork",
             ],
             min_sdk_version: "30",
         },
diff --git a/tv/hdmi/cec/aidl/default/HdmiCecMock.cpp b/tv/hdmi/cec/aidl/default/HdmiCecMock.cpp
index 0212e7e..8a3c6f0 100644
--- a/tv/hdmi/cec/aidl/default/HdmiCecMock.cpp
+++ b/tv/hdmi/cec/aidl/default/HdmiCecMock.cpp
@@ -36,6 +36,7 @@
     ALOGE("HdmiCecMock died");
     auto hdmiCecMock = static_cast<HdmiCecMock*>(cookie);
     hdmiCecMock->mCecThreadRun = false;
+    pthread_join(hdmiCecMock->mThreadId, NULL);
 }
 
 ScopedAStatus HdmiCecMock::addLogicalAddress(CecLogicalAddress addr, Result* _aidl_return) {
@@ -89,7 +90,9 @@
     mCallback = callback;
 
     if (callback != nullptr) {
-        AIBinder_linkToDeath(this->asBinder().get(), mDeathRecipient.get(), 0 /* cookie */);
+        mDeathRecipient =
+                ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(serviceDied));
+        AIBinder_linkToDeath(callback->asBinder().get(), mDeathRecipient.get(), this /* cookie */);
 
         mInputFile = open(CEC_MSG_IN_FIFO, O_RDWR | O_CLOEXEC);
         mOutputFile = open(CEC_MSG_OUT_FIFO, O_RDWR | O_CLOEXEC);
@@ -220,7 +223,7 @@
     int r = -1;
 
     // Open the input pipe
-    while (mInputFile < 0) {
+    while (mCecThreadRun && mInputFile < 0) {
         usleep(1000 * 1000);
         mInputFile = open(CEC_MSG_IN_FIFO, O_RDONLY | O_CLOEXEC);
     }
@@ -257,7 +260,15 @@
 HdmiCecMock::HdmiCecMock() {
     ALOGE("[halimp_aidl] Opening a virtual CEC HAL for testing and virtual machine.");
     mCallback = nullptr;
-    mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(serviceDied));
+    mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(nullptr);
+}
+
+HdmiCecMock::~HdmiCecMock() {
+    ALOGE("[halimp_aidl] HdmiCecMock shutting down.");
+    mCallback = nullptr;
+    mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(nullptr);
+    mCecThreadRun = false;
+    pthread_join(mThreadId, NULL);
 }
 
 }  // namespace implementation
diff --git a/tv/hdmi/cec/aidl/default/HdmiCecMock.h b/tv/hdmi/cec/aidl/default/HdmiCecMock.h
index aca0581..e78b1cf 100644
--- a/tv/hdmi/cec/aidl/default/HdmiCecMock.h
+++ b/tv/hdmi/cec/aidl/default/HdmiCecMock.h
@@ -40,6 +40,7 @@
 
 struct HdmiCecMock : public BnHdmiCec {
     HdmiCecMock();
+    ~HdmiCecMock();
     ::ndk::ScopedAStatus addLogicalAddress(CecLogicalAddress addr, Result* _aidl_return) override;
     ::ndk::ScopedAStatus clearLogicalAddress() override;
     ::ndk::ScopedAStatus enableAudioReturnChannel(int32_t portId, bool enable) override;
diff --git a/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/StaRoamingState.aidl b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/StaRoamingState.aidl
index 1f3d91f..fd7d567 100644
--- a/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/StaRoamingState.aidl
+++ b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/StaRoamingState.aidl
@@ -36,4 +36,5 @@
 enum StaRoamingState {
   DISABLED = 0,
   ENABLED = 1,
+  AGGRESSIVE = 2,
 }
diff --git a/wifi/aidl/android/hardware/wifi/StaRoamingState.aidl b/wifi/aidl/android/hardware/wifi/StaRoamingState.aidl
index d75d323..6872a17 100644
--- a/wifi/aidl/android/hardware/wifi/StaRoamingState.aidl
+++ b/wifi/aidl/android/hardware/wifi/StaRoamingState.aidl
@@ -32,4 +32,9 @@
      * the |StaRoamingConfig| parameters set using |configureRoaming|.
      */
     ENABLED = 1,
+    /**
+     * Driver/Firmware is allowed to roam more aggressively. For instance,
+     * roaming can be triggered at higher RSSI thresholds than normal.
+     */
+    AGGRESSIVE = 2,
 }
diff --git a/wifi/aidl/default/aidl_struct_util.cpp b/wifi/aidl/default/aidl_struct_util.cpp
index 83e1193..a67f59e 100644
--- a/wifi/aidl/default/aidl_struct_util.cpp
+++ b/wifi/aidl/default/aidl_struct_util.cpp
@@ -1147,6 +1147,8 @@
             return legacy_hal::ROAMING_ENABLE;
         case StaRoamingState::DISABLED:
             return legacy_hal::ROAMING_DISABLE;
+        case StaRoamingState::AGGRESSIVE:
+            return legacy_hal::ROAMING_AGGRESSIVE;
     };
     CHECK(false);
 }
diff --git a/wifi/aidl/default/wifi_legacy_hal.h b/wifi/aidl/default/wifi_legacy_hal.h
index 5168a8b..6f012ec 100644
--- a/wifi/aidl/default/wifi_legacy_hal.h
+++ b/wifi/aidl/default/wifi_legacy_hal.h
@@ -186,6 +186,7 @@
 using ::NanTxType;
 using ::NpkSecurityAssociation;
 using ::PASN;
+using ::ROAMING_AGGRESSIVE;
 using ::ROAMING_DISABLE;
 using ::ROAMING_ENABLE;
 using ::RTT_PEER_AP;
diff --git a/wifi/hostapd/aidl/Android.bp b/wifi/hostapd/aidl/Android.bp
index 54895c1..cdc94bb 100644
--- a/wifi/hostapd/aidl/Android.bp
+++ b/wifi/hostapd/aidl/Android.bp
@@ -27,6 +27,9 @@
     srcs: [
         "android/hardware/wifi/hostapd/*.aidl",
     ],
+    imports: [
+        "android.hardware.wifi.common-V1",
+    ],
     stability: "vintf",
     backend: {
         java: {
@@ -40,6 +43,9 @@
         ndk: {
             gen_trace: true,
         },
+        cpp: {
+            enabled: false,
+        },
     },
     versions_with_info: [
         {
@@ -47,5 +53,4 @@
             imports: [],
         },
     ],
-
 }
diff --git a/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/ApInfo.aidl b/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/ApInfo.aidl
index ca20f37..1a66105 100644
--- a/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/ApInfo.aidl
+++ b/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/ApInfo.aidl
@@ -40,4 +40,5 @@
   android.hardware.wifi.hostapd.ChannelBandwidth channelBandwidth;
   android.hardware.wifi.hostapd.Generation generation;
   byte[] apIfaceInstanceMacAddress;
+  @nullable android.hardware.wifi.common.OuiKeyedData[] vendorData;
 }
diff --git a/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/IfaceParams.aidl b/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/IfaceParams.aidl
index 0c88a39..64367bb 100644
--- a/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/IfaceParams.aidl
+++ b/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/IfaceParams.aidl
@@ -37,4 +37,5 @@
   String name;
   android.hardware.wifi.hostapd.HwModeParams hwModeParams;
   android.hardware.wifi.hostapd.ChannelParams[] channelParams;
+  @nullable android.hardware.wifi.common.OuiKeyedData[] vendorData;
 }
diff --git a/wifi/hostapd/aidl/android/hardware/wifi/hostapd/ApInfo.aidl b/wifi/hostapd/aidl/android/hardware/wifi/hostapd/ApInfo.aidl
index a6fe63b..f2b2ee6 100644
--- a/wifi/hostapd/aidl/android/hardware/wifi/hostapd/ApInfo.aidl
+++ b/wifi/hostapd/aidl/android/hardware/wifi/hostapd/ApInfo.aidl
@@ -16,6 +16,7 @@
 
 package android.hardware.wifi.hostapd;
 
+import android.hardware.wifi.common.OuiKeyedData;
 import android.hardware.wifi.hostapd.ChannelBandwidth;
 import android.hardware.wifi.hostapd.Generation;
 
@@ -57,4 +58,9 @@
      * MAC Address of the apIfaceInstance.
      */
     byte[] apIfaceInstanceMacAddress;
+
+    /**
+     * Optional vendor-specific information.
+     */
+    @nullable OuiKeyedData[] vendorData;
 }
diff --git a/wifi/hostapd/aidl/android/hardware/wifi/hostapd/IfaceParams.aidl b/wifi/hostapd/aidl/android/hardware/wifi/hostapd/IfaceParams.aidl
index a8abec3..3f8ee39 100644
--- a/wifi/hostapd/aidl/android/hardware/wifi/hostapd/IfaceParams.aidl
+++ b/wifi/hostapd/aidl/android/hardware/wifi/hostapd/IfaceParams.aidl
@@ -16,6 +16,7 @@
 
 package android.hardware.wifi.hostapd;
 
+import android.hardware.wifi.common.OuiKeyedData;
 import android.hardware.wifi.hostapd.ChannelParams;
 import android.hardware.wifi.hostapd.HwModeParams;
 
@@ -36,4 +37,8 @@
      * The list of the channel params for the dual interfaces.
      */
     ChannelParams[] channelParams;
+    /**
+     * Optional vendor-specific configuration parameters.
+     */
+    @nullable OuiKeyedData[] vendorData;
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl
index 80d8546..8aa593f 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl
@@ -45,6 +45,9 @@
   oneway void onGroupStarted(in String groupIfname, in boolean isGroupOwner, in byte[] ssid, in int frequency, in byte[] psk, in String passphrase, in byte[] goDeviceAddress, in boolean isPersistent);
   oneway void onInvitationReceived(in byte[] srcAddress, in byte[] goDeviceAddress, in byte[] bssid, in int persistentNetworkId, in int operatingFrequency);
   oneway void onInvitationResult(in byte[] bssid, in android.hardware.wifi.supplicant.P2pStatusCode status);
+  /**
+   * @deprecated This callback is deprecated from AIDL v3, newer HAL should call onProvisionDiscoveryCompletedEvent.
+   */
   oneway void onProvisionDiscoveryCompleted(in byte[] p2pDeviceAddress, in boolean isRequest, in android.hardware.wifi.supplicant.P2pProvDiscStatusCode status, in android.hardware.wifi.supplicant.WpsConfigMethods configMethods, in String generatedPin);
   oneway void onR2DeviceFound(in byte[] srcAddress, in byte[] p2pDeviceAddress, in byte[] primaryDeviceType, in String deviceName, in android.hardware.wifi.supplicant.WpsConfigMethods configMethods, in byte deviceCapabilities, in android.hardware.wifi.supplicant.P2pGroupCapabilityMask groupCapabilities, in byte[] wfdDeviceInfo, in byte[] wfdR2DeviceInfo);
   oneway void onServiceDiscoveryResponse(in byte[] srcAddress, in char updateIndicator, in byte[] tlvs);
@@ -61,4 +64,5 @@
   oneway void onGroupStartedWithParams(in android.hardware.wifi.supplicant.P2pGroupStartedEventParams groupStartedEventParams);
   oneway void onPeerClientJoined(in android.hardware.wifi.supplicant.P2pPeerClientJoinedEventParams clientJoinedEventParams);
   oneway void onPeerClientDisconnected(in android.hardware.wifi.supplicant.P2pPeerClientDisconnectedEventParams clientDisconnectedEventParams);
+  oneway void onProvisionDiscoveryCompletedEvent(in android.hardware.wifi.supplicant.P2pProvisionDiscoveryCompletedEventParams provisionDiscoveryCompletedEventParams);
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIface.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIface.aidl
index 1616b26..1f3aa48 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIface.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIface.aidl
@@ -54,6 +54,9 @@
   android.hardware.wifi.supplicant.IfaceType getType();
   android.hardware.wifi.supplicant.WpaDriverCapabilitiesMask getWpaDriverCapabilities();
   void initiateAnqpQuery(in byte[] macAddress, in android.hardware.wifi.supplicant.AnqpInfoId[] infoElements, in android.hardware.wifi.supplicant.Hs20AnqpSubtypes[] subTypes);
+  /**
+   * @deprecated No longer in use.
+   */
   void initiateHs20IconQuery(in byte[] macAddress, in String fileName);
   void initiateTdlsDiscover(in byte[] macAddress);
   void initiateTdlsSetup(in byte[] macAddress);
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 1c23223..898c2d4 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
@@ -53,6 +53,9 @@
   oneway void onExtRadioWorkStart(in int id);
   oneway void onExtRadioWorkTimeout(in int id);
   oneway void onHs20DeauthImminentNotice(in byte[] bssid, in int reasonCode, in int reAuthDelayInSec, in String url);
+  /**
+   * @deprecated No longer in use.
+   */
   oneway void onHs20IconQueryDone(in byte[] bssid, in String fileName, in byte[] data);
   oneway void onHs20SubscriptionRemediation(in byte[] bssid, in android.hardware.wifi.supplicant.OsuMethod osuMethod, in String url);
   oneway void onHs20TermsAndConditionsAcceptanceRequestedNotification(in byte[] bssid, in String url);
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl
index bfc05a4..23017e8 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl
@@ -129,6 +129,7 @@
   void setRoamingConsortiumSelection(in byte[] selectedRcoi);
   void setMinimumTlsVersionEapPhase1Param(android.hardware.wifi.supplicant.TlsVersion tlsVersion);
   void setStrictConservativePeerMode(in boolean enable);
+  void disableEht();
   const int SSID_MAX_LEN_IN_BYTES = 32;
   const int PSK_PASSPHRASE_MIN_LEN_IN_BYTES = 8;
   const int PSK_PASSPHRASE_MAX_LEN_IN_BYTES = 63;
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pProvisionDiscoveryCompletedEventParams.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pProvisionDiscoveryCompletedEventParams.aidl
new file mode 100644
index 0000000..587c7c6
--- /dev/null
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pProvisionDiscoveryCompletedEventParams.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 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.wifi.supplicant;
+@VintfStability
+parcelable P2pProvisionDiscoveryCompletedEventParams {
+  byte[6] p2pDeviceAddress;
+  boolean isRequest;
+  android.hardware.wifi.supplicant.P2pProvDiscStatusCode status;
+  android.hardware.wifi.supplicant.WpsConfigMethods configMethods;
+  String generatedPin;
+  String groupInterfaceName;
+}
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl
index 810fe48..8befc0d 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl
@@ -21,6 +21,7 @@
 import android.hardware.wifi.supplicant.P2pPeerClientDisconnectedEventParams;
 import android.hardware.wifi.supplicant.P2pPeerClientJoinedEventParams;
 import android.hardware.wifi.supplicant.P2pProvDiscStatusCode;
+import android.hardware.wifi.supplicant.P2pProvisionDiscoveryCompletedEventParams;
 import android.hardware.wifi.supplicant.P2pStatusCode;
 import android.hardware.wifi.supplicant.WpsConfigMethods;
 import android.hardware.wifi.supplicant.WpsDevPasswordId;
@@ -144,6 +145,9 @@
 
     /**
      * Used to indicate the completion of a P2P provision discovery request.
+     * <p>
+     * @deprecated This callback is deprecated from AIDL v3, newer HAL should call
+     * onProvisionDiscoveryCompletedEvent.
      *
      * @param p2pDeviceAddress P2P device address.
      * @param isRequest Whether we received or sent the provision discovery.
@@ -275,4 +279,13 @@
      */
     void onPeerClientDisconnected(
             in P2pPeerClientDisconnectedEventParams clientDisconnectedEventParams);
+
+    /**
+     * Used to indicate the completion of a P2P provision discovery request.
+     *
+     * @param provisionDiscoveryCompletedEventParams Parameters associated with
+     *        P2P provision discovery frame notification.
+     */
+    void onProvisionDiscoveryCompletedEvent(
+            in P2pProvisionDiscoveryCompletedEventParams provisionDiscoveryCompletedEventParams);
 }
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIface.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIface.aidl
index 06ab8fb..d7b4e62 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIface.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIface.aidl
@@ -304,6 +304,8 @@
      * The icon data fetched must be returned in the
      * |ISupplicantStaIfaceCallback.onHs20IconQueryDone| callback.
      *
+     * @deprecated No longer in use.
+     *
      * @param macAddress MAC address of the access point.
      * @param fileName Name of the file to request from the access point.
      * @throws ServiceSpecificException with one of the following values:
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
index 17a220d..58893eb 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
@@ -198,6 +198,8 @@
     /**
      * Used to indicate the result of Hotspot 2.0 Icon query.
      *
+     * @deprecated No longer in use.
+     *
      * @param bssid BSSID of the access point.
      * @param fileName Name of the file that was requested.
      * @param data Icon data fetched from the access point.
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl
index 750cf72..9fece4e 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl
@@ -1141,4 +1141,16 @@
      *         |SupplicantStatusCode.FAILURE_NETWORK_INVALID|
      */
     void setStrictConservativePeerMode(in boolean enable);
+
+    /**
+     * Disables Extremely High Throughput (EHT) mode, aka Wi-Fi 7 support, for the network. When
+     * EHT is disabled, the device ceases to transmit EHT related Information Elements (IEs),
+     * including multi-link IEs and EHT capability, in subsequent messages such as (Re)Association
+     * requests to the Access Point (AP).
+     *
+     * @throws ServiceSpecificException with one of the following values:
+     *         |SupplicantStatusCode.FAILURE_UNKNOWN|,
+     *         |SupplicantStatusCode.FAILURE_NETWORK_INVALID|
+     */
+    void disableEht();
 }
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/P2pProvisionDiscoveryCompletedEventParams.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/P2pProvisionDiscoveryCompletedEventParams.aidl
new file mode 100644
index 0000000..7fa7f22
--- /dev/null
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/P2pProvisionDiscoveryCompletedEventParams.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.wifi.supplicant;
+
+import android.hardware.wifi.supplicant.P2pProvDiscStatusCode;
+import android.hardware.wifi.supplicant.WpsConfigMethods;
+
+/**
+ * Parameters passed as a part of P2P provision discovery frame notification.
+ */
+@VintfStability
+parcelable P2pProvisionDiscoveryCompletedEventParams {
+    /**
+     * P2P device interface MAC address of the device who sent the request or responded to our
+     * request.
+     */
+    byte[6] p2pDeviceAddress;
+    /** True if this is a request, false if this is a response. */
+    boolean isRequest;
+    /** Status of the provision discovery */
+    P2pProvDiscStatusCode status;
+    /** Mask of WPS configuration methods supported */
+    WpsConfigMethods configMethods;
+    /** 8-digit pin generated */
+    String generatedPin;
+    /**
+     * Interface name of this device group owner. (For ex: p2p-p2p0-1)
+     * This field is filled only when the provision discovery request is received
+     * with P2P Group ID attribute. i.e., when the peer device is joining this
+     * device operating P2P group.
+     * Refer to WFA Wi-Fi_Direct_Specification_v1.9 section 3.2.1 for more details.
+     */
+    String groupInterfaceName;
+}
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
index d3dd2e0..0db1653 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
@@ -41,6 +41,7 @@
 using aidl::android::hardware::wifi::supplicant::P2pPeerClientDisconnectedEventParams;
 using aidl::android::hardware::wifi::supplicant::P2pPeerClientJoinedEventParams;
 using aidl::android::hardware::wifi::supplicant::P2pProvDiscStatusCode;
+using aidl::android::hardware::wifi::supplicant::P2pProvisionDiscoveryCompletedEventParams;
 using aidl::android::hardware::wifi::supplicant::P2pStatusCode;
 using aidl::android::hardware::wifi::supplicant::SupplicantStatusCode;
 using aidl::android::hardware::wifi::supplicant::WpsConfigMethods;
@@ -193,6 +194,11 @@
             override {
         return ndk::ScopedAStatus::ok();
     }
+    ::ndk::ScopedAStatus onProvisionDiscoveryCompletedEvent(
+            const P2pProvisionDiscoveryCompletedEventParams&
+            /* provisionDiscoveryCompletedEventParams */) override {
+        return ndk::ScopedAStatus::ok();
+    }
 };
 
 class SupplicantP2pIfaceAidlTest : public testing::TestWithParam<std::string> {
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp
index 973b56a..7574141 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp
@@ -822,6 +822,13 @@
               tlsV13Supported);
 }
 
+/*
+ * disableEht
+ */
+TEST_P(SupplicantStaNetworkAidlTest, DisableEht) {
+    EXPECT_TRUE(sta_network_->disableEht().isOk());
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantStaNetworkAidlTest);
 INSTANTIATE_TEST_SUITE_P(Supplicant, SupplicantStaNetworkAidlTest,
                          testing::ValuesIn(android::getAidlHalInstanceNames(