Merge "Add DRIVER_DROWSINESS_ATTENTION_SYSTEM_ENABLED to HAL layer." 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/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/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/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/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/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/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 ad3f4ac..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>;
 
@@ -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/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/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/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/biometrics/face/aidl/default/FakeFaceEngine.cpp b/biometrics/face/aidl/default/FakeFaceEngine.cpp
index 578231d..dc524bb 100644
--- a/biometrics/face/aidl/default/FakeFaceEngine.cpp
+++ b/biometrics/face/aidl/default/FakeFaceEngine.cpp
@@ -53,15 +53,6 @@
                                 const std::vector<Feature>& /*features*/,
                                 const std::future<void>& cancel) {
     BEGIN_OP(FaceHalProperties::operation_start_enroll_latency().value_or(0));
-    // format is "<id>,<bucket_id>:<delay>:<succeeds>,<bucket_id>:<delay>:<succeeds>...
-    auto nextEnroll = FaceHalProperties::next_enrollment().value_or("");
-    // Erase the next enrollment
-    FaceHalProperties::next_enrollment({});
-
-    AuthenticationFrame frame;
-    frame.data.acquiredInfo = AcquiredInfo::START;
-    frame.data.vendorCode = 0;
-    cb->onAuthenticationFrame(frame);
 
     // Do proper HAT verification in the real implementation.
     if (hat.mac.empty()) {
@@ -70,66 +61,81 @@
         return;
     }
 
-    if (FaceHalProperties::operation_enroll_fails().value_or(false)) {
-        LOG(ERROR) << "Fail: operation_enroll_fails";
+    // Format: <id>:<progress_ms-[acquiredInfo,...],...:<success>
+    // ------:-----------------------------------------:--------------
+    //          |           |                              |--->enrollment success (true/false)
+    //          |           |--> progress_steps
+    //          |
+    //          |-->enrollment id
+    //
+    //
+    //   progress_steps
+    //        <progress_duration>-[acquiredInfo,...]+
+    //        ----------------------------  ---------------------
+    //                 |                            |-> sequence of acquiredInfo code
+    //                 | --> time duration of the step in ms
+    //
+    //        E.g.   1:2000-[21,1108,5,6,1],1000-[1113,4,1]:true
+    //              A success enrollement of id 1 by 2 steps
+    //                    1st step lasts 2000ms with acquiredInfo codes (21,1108,5,6,1)
+    //                    2nd step lasts 1000ms with acquiredInfo codes (1113,4,1)
+    //
+    std::string defaultNextEnrollment =
+            "1:1000-[21,7,1,1103],1500-[1108,1],2000-[1113,1],2500-[1118,1]:true";
+    auto nextEnroll = FaceHalProperties::next_enrollment().value_or(defaultNextEnrollment);
+    auto parts = Util::split(nextEnroll, ":");
+    if (parts.size() != 3) {
+        LOG(ERROR) << "Fail: invalid next_enrollment:" << nextEnroll;
         cb->onError(Error::VENDOR, 0 /* vendorError */);
         return;
     }
-
-    auto parts = Util::split(nextEnroll, ",");
-    if (parts.size() < 2) {
-        LOG(ERROR) << "Fail: invalid next_enrollment for : " << nextEnroll;
-        cb->onError(Error::VENDOR, 0 /* vendorError */);
-        return;
-    }
-
     auto enrollmentId = std::stoi(parts[0]);
-    const int numBuckets = parts.size() - 1;
-    for (size_t i = 1; i < parts.size(); i++) {
-        auto enrollHit = Util::split(parts[i], ":");
-        if (enrollHit.size() != 3) {
-            LOG(ERROR) << "Error when unpacking enrollment hit: " << parts[i];
-            cb->onError(Error::VENDOR, 0 /* vendorError */);
-        }
-        std::string bucket = enrollHit[0];
-        std::string delay = enrollHit[1];
-        std::string succeeds = enrollHit[2];
+    auto progress = Util::parseEnrollmentCapture(parts[1]);
+    for (size_t i = 0; i < progress.size(); i += 2) {
+        auto left = (progress.size() - i) / 2 - 1;
+        auto duration = progress[i][0];
+        auto acquired = progress[i + 1];
+        auto N = acquired.size();
 
-        SLEEP_MS(std::stoi(delay));
+        for (int j = 0; j < N; j++) {
+            SLEEP_MS(duration / N);
 
-        if (shouldCancel(cancel)) {
-            LOG(ERROR) << "Fail: cancel";
-            cb->onError(Error::CANCELED, 0 /* vendorCode */);
-            return;
+            if (shouldCancel(cancel)) {
+                LOG(ERROR) << "Fail: cancel";
+                cb->onError(Error::CANCELED, 0 /* vendorCode */);
+                return;
+            }
+            EnrollmentFrame frame = {};
+            auto ac = convertAcquiredInfo(acquired[j]);
+            frame.data.acquiredInfo = ac.first;
+            frame.data.vendorCode = ac.second;
+            frame.stage = (i == 0 && j == 0) ? EnrollmentStage::FIRST_FRAME_RECEIVED
+                          : (i == progress.size() - 2 && j == N - 1)
+                                  ? EnrollmentStage::ENROLLMENT_FINISHED
+                                  : EnrollmentStage::WAITING_FOR_CENTERING;
+            cb->onEnrollmentFrame(frame);
         }
 
-        if (!IS_TRUE(succeeds)) {  // end and failed
-            LOG(ERROR) << "Fail: requested by caller: " << parts[i];
+        if (left == 0 && !IS_TRUE(parts[2])) {  // end and failed
+            LOG(ERROR) << "Fail: requested by caller: " << nextEnroll;
+            FaceHalProperties::next_enrollment({});
             cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
-            return;
-        }
-
-        EnrollmentFrame frame;
-
-        frame.data.acquiredInfo = AcquiredInfo::GOOD;
-        frame.data.vendorCode = 0;
-        cb->onEnrollmentFrame(frame);
-
-        frame.data.acquiredInfo = AcquiredInfo::VENDOR;
-        frame.data.vendorCode = std::stoi(bucket);
-        cb->onEnrollmentFrame(frame);
-
-        int remainingBuckets = numBuckets - i;
-        if (remainingBuckets > 0) {
-            cb->onEnrollmentProgress(enrollmentId, remainingBuckets);
+        } else {  // progress and update props if last time
+            LOG(INFO) << "onEnroll: " << enrollmentId << " left: " << left;
+            if (left == 0) {
+                auto enrollments = FaceHalProperties::enrollments();
+                enrollments.emplace_back(enrollmentId);
+                FaceHalProperties::enrollments(enrollments);
+                FaceHalProperties::next_enrollment({});
+                // change authenticatorId after new enrollment
+                auto id = FaceHalProperties::authenticator_id().value_or(0);
+                auto newId = id + 1;
+                FaceHalProperties::authenticator_id(newId);
+                LOG(INFO) << "Enrolled: " << enrollmentId;
+            }
+            cb->onEnrollmentProgress(enrollmentId, left);
         }
     }
-
-    auto enrollments = FaceHalProperties::enrollments();
-    enrollments.push_back(enrollmentId);
-    FaceHalProperties::enrollments(enrollments);
-    LOG(INFO) << "enrolled : " << enrollmentId;
-    cb->onEnrollmentProgress(enrollmentId, 0);
 }
 
 void FakeFaceEngine::authenticateImpl(ISessionCallback* cb, int64_t /*operationId*/,
diff --git a/biometrics/face/aidl/default/README.md b/biometrics/face/aidl/default/README.md
index 516a7aa..9225258 100644
--- a/biometrics/face/aidl/default/README.md
+++ b/biometrics/face/aidl/default/README.md
@@ -1,30 +1,35 @@
 # Face Virtual HAL (VHAL)
 
-This is a virtual HAL implementation that is backed by system properties
-instead of actual hardware. It's intended for testing and UI development
-on debuggable builds to allow devices to masquerade as alternative device
-types and for emulators.
-Note: The virtual face HAL feature development will be done in phases. Refer to this doc often for
-the latest supported features
+This is a virtual HAL implementation that is backed by system properties instead
+of actual hardware. It's intended for testing and UI development on debuggable
+builds to allow devices to masquerade as alternative device types and for
+emulators. Note: The virtual face HAL feature development will be done in
+phases. Refer to this doc often for the latest supported features
 
 ## Supported Devices
 
-The face virtual hal is automatically built in in all debug builds (userdebug and eng) for the latest pixel devices and CF.
-The instructions in this doc applies  to all
+The face virtual hal is automatically built in in all debug builds (userdebug<br/>
+and eng) for the latest pixel devices and CF. The instructions in this doc<br/>
+applies to all
 
 ## Enabling Face Virtual HAL
 
-On pixel devicse (non-CF), by default (after manufacture reset), Face VHAL is not enabled. Therefore real Face HAL is used.
-Face VHAL enabling is gated by the following two AND conditions:
-1. The Face VHAL feature flag (as part of Trunk-development strategy) must be tured until the flags life-cycle ends.
-2. The Face VHAL must be enabled via sysprop
-See adb commands below
+On pixel devicse (non-CF), by default (after manufacture reset), Face VHAL is <br/>
+not enabled. Therefore real Face HAL is used. Face VHAL enabling is gated by the<br/>
+following two AND conditions:<br/>
+1. The Face VHAL feature flag (as part ofTrunk-development strategy) must be<br/>
+   turned on until the flags life-cycle ends.
+2. The Face VHAL must be enabled via sysprop.
 
-##Getting Stared
+See the adb commands below
 
-A basic use case for a successful authentication via Face VHAL is given as an exmple below.
+## Getting Stared
+
+A basic use case for a successful authentication via Face VHAL is given as an
+exmple below.
 
 ### Enabling VHAL
+
 ```shell
 $ adb root
 $ adb shell device_config put biometrics_framework com.android.server.biometrics.face_vhal_feature true
@@ -35,6 +40,7 @@
 ```
 
 ### Direct Enrollment
+
 ```shell
 $ adb shell locksettings set-pin 0000
 $ adb shell setprop persist.vendor.face.virtual.enrollments 1
@@ -42,22 +48,47 @@
 ```
 
 ## Authenticating
-To authenticate successfully, the captured (hit) must match the enrollment id set above. To  trigger
-authentication failure, set the hit id to a different value.
+
+To authenticate successfully, the captured (hit) must match the enrollment id<br/>
+set above. To trigger authentication failure, set the hit id to a different value.
 ```shell
 $ adb shell setprop vendor.face.virtual.operation_authenticate_duration 800
 $ adb shell setprop vendor.face.virtual.enrollment_hit 1
 ```
-Refer to face.sysprop for full supported features of authentication, such as error and acquiredInfo insertion
+
+### AcquiredInfo
+AcquiredInfo codes can be sent during authentication by specifying the sysprop.<br/>
+The codes is sent in sequence and in the interval of operation_authentication_duration/numberOfAcquiredInfoCode
+```shell
+$ adb shell setprop vendor.face.virtual.operation_authenticate_acquired 6,9,1013
+```
+Refer to [AcquiredInfo.aidl](https://source.corp.google.com/h/googleplex-android/platform/superproject/main/+/main:hardware/interfaces/biometrics/face/aidl/android/hardware/biometrics/face/AcquiredInfo.aidl) for full face acquiredInfo codes.
+Note: For vendor specific acquired info, acquiredInfo = 1000 + vendorCode.
+
+### Error Insertion
+Error can be inserted during authentction by specifying the authenticate_error sysprop.
+```shell
+$ adb shell setprop vendor.face.virtual.operation_authenticate_error 4
+```
+Refer to [Error.aidl](https://source.corp.google.com/h/googleplex-android/platform/superproject/main/+/main:hardware/interfaces/biometrics/face/aidl/android/hardware/biometrics/face/Error.aidl) for full face error codes
 
 
-## Enrollment via Setup
+## Enrollment via Settings
+
+Enrollment process is specified by sysprop `next_enrollment` in the following format
 
 ```shell
-# authenticar_id,bucket_id:duration:(true|false)....
-$ adb shell setprop vendor.face.virtual.next_enrollment 1,0:500:true,5:250:true,10:150:true,15:500:true
-$ walk thru the manual enrollment process by following screen instructions
+Format: <id>:<progress_ms-[acquiredInfo,...],...:<success>
+        ----:-----------------------------------:---------
+        |           |                               |--->sucess (true/false)
+        |           |--> progress_step(s)
+        |
+        |-->enrollment_id
 
-# If you would like to get rid of the enrollment, run the follwoing command
-$ adb shell setprop persist.vendor.face.virtual.enrollments \"\"
+E.g.
+$ adb shell setprop vendor.face.virtual.next_enrollment 1:6000-[21,8,1,1108,1,10,1113,1,1118,1124]:true
 ```
+If next_enrollment prop is not set, the following default value is used:<br/>
+&nbsp;&nbsp;defaultNextEnrollment="1:1000-[21,7,1,1103],1500-[1108,1],2000-[1113,1],2500-[1118,1]:true"<br/>
+Note: Enrollment data and configuration can be supported upon request in case of needs
+
diff --git a/biometrics/face/aidl/default/tests/FakeFaceEngineTest.cpp b/biometrics/face/aidl/default/tests/FakeFaceEngineTest.cpp
index 6897dc4..69c9bf4 100644
--- a/biometrics/face/aidl/default/tests/FakeFaceEngineTest.cpp
+++ b/biometrics/face/aidl/default/tests/FakeFaceEngineTest.cpp
@@ -45,6 +45,7 @@
     };
     ::ndk::ScopedAStatus onEnrollmentProgress(int32_t enrollmentId, int32_t remaining) override {
         if (remaining == 0) mLastEnrolled = enrollmentId;
+        mRemaining = remaining;
         return ndk::ScopedAStatus::ok();
     };
 
@@ -128,6 +129,7 @@
     bool mAuthenticatorIdInvalidated = false;
     bool mLockoutPermanent = false;
     int mInteractionDetectedCount = 0;
+    int mRemaining = -1;
 };
 
 class FakeFaceEngineTest : public ::testing::Test {
@@ -193,7 +195,7 @@
 }
 
 TEST_F(FakeFaceEngineTest, Enroll) {
-    FaceHalProperties::next_enrollment("1,0:30:true,1:0:true,2:0:true,3:0:true,4:0:true");
+    FaceHalProperties::next_enrollment("1,0:1000-[21,5,6,7,1],1100-[1118,1108,1]:true");
     keymaster::HardwareAuthToken hat{.mac = {2, 4}};
     mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
                        mCancel.get_future());
@@ -201,10 +203,11 @@
     ASSERT_EQ(1, FaceHalProperties::enrollments().size());
     ASSERT_EQ(1, FaceHalProperties::enrollments()[0].value());
     ASSERT_EQ(1, mCallback->mLastEnrolled);
+    ASSERT_EQ(0, mCallback->mRemaining);
 }
 
 TEST_F(FakeFaceEngineTest, EnrollFails) {
-    FaceHalProperties::next_enrollment("1,0:30:true,1:0:true,2:0:true,3:0:true,4:0:false");
+    FaceHalProperties::next_enrollment("1,0:1000-[21,5,6,7,1],1100-[1118,1108,1]:false");
     keymaster::HardwareAuthToken hat{.mac = {2, 4}};
     mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
                        mCancel.get_future());
@@ -213,7 +216,7 @@
 }
 
 TEST_F(FakeFaceEngineTest, EnrollCancel) {
-    FaceHalProperties::next_enrollment("1,0:30:true,1:0:true,2:0:true,3:0:true,4:0:false");
+    FaceHalProperties::next_enrollment("1:2000-[21,8,9],300:false");
     keymaster::HardwareAuthToken hat{.mac = {2, 4}};
     mCancel.set_value();
     mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
@@ -221,7 +224,7 @@
     ASSERT_EQ(Error::CANCELED, mCallback->mError);
     ASSERT_EQ(-1, mCallback->mLastEnrolled);
     ASSERT_EQ(0, FaceHalProperties::enrollments().size());
-    ASSERT_FALSE(FaceHalProperties::next_enrollment().has_value());
+    ASSERT_TRUE(FaceHalProperties::next_enrollment().has_value());
 }
 
 TEST_F(FakeFaceEngineTest, Authenticate) {
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 f82e767..4d6d81d 100644
--- a/broadcastradio/aidl/default/BroadcastRadio.cpp
+++ b/broadcastradio/aidl/default/BroadcastRadio.cpp
@@ -66,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"},
     });
@@ -245,7 +240,10 @@
         }
         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({});
     }
@@ -276,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() << "...";
 
@@ -308,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);
@@ -323,7 +347,9 @@
     }
     // The list is not sorted here since it has already stored in VirtualRadio.
     bool hasAmFmFrequency = utils::hasAmFmFrequency(current);
-    uint32_t currentFreq = hasAmFmFrequency ? utils::getAmFmFrequency(current) : 0;
+    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) {
@@ -335,18 +361,22 @@
             // 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 && hasAmFmFrequency) {
-                auto firstFound = found;
-                while (utils::getAmFmFrequency(found->selector) == currentFreq) {
-                    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;
+            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;
+                        }
                     }
                 }
             }
@@ -365,9 +395,11 @@
             // 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)) {
-                uint32_t nextFreq = utils::getAmFmFrequency(found->selector);
-                if (nextFreq != currentFreq) {
+            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);
@@ -400,16 +432,28 @@
 }
 
 void BroadcastRadio::jumpToFirstSubChannelLocked(vector<VirtualProgram>::const_iterator& it) const {
-    if (!utils::hasAmFmFrequency(it->selector) || it == mProgramList.begin()) {
+    if (it == mProgramList.begin()) {
         return;
     }
-    uint32_t currentFrequency = utils::getAmFmFrequency(it->selector);
-    it--;
-    while (it != mProgramList.begin() && utils::hasAmFmFrequency(it->selector) &&
-           utils::getAmFmFrequency(it->selector) == currentFrequency) {
+    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++;
     }
-    it++;
 }
 
 ScopedAStatus BroadcastRadio::seek(bool directionUp, bool skipSubChannel) {
@@ -456,7 +500,7 @@
             lock_guard<mutex> lk(mMutex);
             programInfo = tuneInternalLocked(nextProgram.selector);
         }
-        callback->onCurrentProgramInfoChanged(programInfo);
+        handleProgramInfoUpdateRadioCallback(programInfo, callback);
     };
     mTuningThread->schedule(task, cancelTask, kSeekDelayTimeMs);
 
@@ -512,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);
diff --git a/broadcastradio/aidl/default/BroadcastRadio.h b/broadcastradio/aidl/default/BroadcastRadio.h
index e43d7c5..60ea907 100644
--- a/broadcastradio/aidl/default/BroadcastRadio.h
+++ b/broadcastradio/aidl/default/BroadcastRadio.h
@@ -91,6 +91,9 @@
     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
diff --git a/broadcastradio/aidl/default/VirtualProgram.cpp b/broadcastradio/aidl/default/VirtualProgram.cpp
index 19c1dcf..dca431d 100644
--- a/broadcastradio/aidl/default/VirtualProgram.cpp
+++ b/broadcastradio/aidl/default/VirtualProgram.cpp
@@ -111,14 +111,25 @@
         return freq1 < freq2 || (freq1 == freq2 && (l.primaryId.type < r.primaryId.type ||
                                                     subChannel1 < subChannel2));
     } else if (l.primaryId.type == IdentifierType::DAB_SID_EXT &&
-               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;
         }
-        return utils::getId(l, IdentifierType::DAB_ENSEMBLE) <
-               utils::getId(r, IdentifierType::DAB_ENSEMBLE);
+        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) {
diff --git a/broadcastradio/aidl/default/VirtualRadio.cpp b/broadcastradio/aidl/default/VirtualRadio.cpp
index ba43d8a..d6e58cd 100644
--- a/broadcastradio/aidl/default/VirtualRadio.cpp
+++ b/broadcastradio/aidl/default/VirtualRadio.cpp
@@ -16,6 +16,7 @@
 
 #include "VirtualRadio.h"
 #include <broadcastradio-utils-aidl/Utils.h>
+#include <unordered_set>
 
 namespace aidl::android::hardware::broadcastradio {
 
@@ -23,6 +24,7 @@
 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)
@@ -62,6 +64,20 @@
     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
@@ -103,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 a139e00..bb43903 100644
--- a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
+++ b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
@@ -163,6 +163,12 @@
 
 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);
diff --git a/broadcastradio/common/utilsaidl/src/Utils.cpp b/broadcastradio/common/utilsaidl/src/Utils.cpp
index 4c99514..76c3c6a 100644
--- a/broadcastradio/common/utilsaidl/src/Utils.cpp
+++ b/broadcastradio/common/utilsaidl/src/Utils.cpp
@@ -489,6 +489,21 @@
     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
diff --git a/camera/device/aidl/Android.bp b/camera/device/aidl/Android.bp
index 1a665fb..4115c65 100644
--- a/camera/device/aidl/Android.bp
+++ b/camera/device/aidl/Android.bp
@@ -11,7 +11,7 @@
     name: "android.hardware.camera.device",
     vendor_available: true,
     srcs: ["android/hardware/camera/device/*.aidl"],
-    frozen: true,
+    frozen: false,
     stability: "vintf",
     imports: [
         "android.hardware.common-V2",
diff --git a/camera/device/aidl/aidl_api/android.hardware.camera.device/current/android/hardware/camera/device/ConfigureStreamsRet.aidl b/camera/device/aidl/aidl_api/android.hardware.camera.device/current/android/hardware/camera/device/ConfigureStreamsRet.aidl
new file mode 100644
index 0000000..5535a30
--- /dev/null
+++ b/camera/device/aidl/aidl_api/android.hardware.camera.device/current/android/hardware/camera/device/ConfigureStreamsRet.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.camera.device;
+@VintfStability
+parcelable ConfigureStreamsRet {
+  android.hardware.camera.device.HalStream[] halStreams;
+  boolean enableHalBufferManager = false;
+}
diff --git a/camera/device/aidl/aidl_api/android.hardware.camera.device/current/android/hardware/camera/device/ICameraDevice.aidl b/camera/device/aidl/aidl_api/android.hardware.camera.device/current/android/hardware/camera/device/ICameraDevice.aidl
index 51c6067..903ef60 100644
--- a/camera/device/aidl/aidl_api/android.hardware.camera.device/current/android/hardware/camera/device/ICameraDevice.aidl
+++ b/camera/device/aidl/aidl_api/android.hardware.camera.device/current/android/hardware/camera/device/ICameraDevice.aidl
@@ -43,4 +43,6 @@
   void setTorchMode(boolean on);
   void turnOnTorchWithStrengthLevel(int torchStrength);
   int getTorchStrengthLevel();
+  android.hardware.camera.device.CameraMetadata constructDefaultRequestSettings(in android.hardware.camera.device.RequestTemplate type);
+  boolean isStreamCombinationWithSettingsSupported(in android.hardware.camera.device.StreamConfiguration streams);
 }
diff --git a/camera/device/aidl/aidl_api/android.hardware.camera.device/current/android/hardware/camera/device/ICameraDeviceSession.aidl b/camera/device/aidl/aidl_api/android.hardware.camera.device/current/android/hardware/camera/device/ICameraDeviceSession.aidl
index 2196d37..b6ae734 100644
--- a/camera/device/aidl/aidl_api/android.hardware.camera.device/current/android/hardware/camera/device/ICameraDeviceSession.aidl
+++ b/camera/device/aidl/aidl_api/android.hardware.camera.device/current/android/hardware/camera/device/ICameraDeviceSession.aidl
@@ -45,4 +45,5 @@
   oneway void signalStreamFlush(in int[] streamIds, in int streamConfigCounter);
   android.hardware.camera.device.ICameraOfflineSession switchToOffline(in int[] streamsToKeep, out android.hardware.camera.device.CameraOfflineSessionInfo offlineSessionInfo);
   void repeatingRequestEnd(in int frameNumber, in int[] streamIds);
+  android.hardware.camera.device.ConfigureStreamsRet configureStreamsV2(in android.hardware.camera.device.StreamConfiguration requestedConfiguration);
 }
diff --git a/camera/device/aidl/android/hardware/camera/device/ConfigureStreamsRet.aidl b/camera/device/aidl/android/hardware/camera/device/ConfigureStreamsRet.aidl
new file mode 100644
index 0000000..8f462ec
--- /dev/null
+++ b/camera/device/aidl/android/hardware/camera/device/ConfigureStreamsRet.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.
+ */
+
+package android.hardware.camera.device;
+
+import android.hardware.camera.device.HalStream;
+
+/**
+ * ConfigureStreamsRet.
+ *
+ * Parcelable returned by the 'configureStreamsV2' call.
+ * This contains information which informs the camera framework
+ * about properties of each stream configured and also whether the
+ * the hal buffer manager must be used for the session configured.
+ */
+@VintfStability
+parcelable ConfigureStreamsRet {
+    /**
+     * A vector of HalStream Parcelables, which contain information
+     * about the stream parameters desired by the HAL such as usage flags,
+     * overridden format, maximum buffers etc.
+     */
+    HalStream[] halStreams;
+    /**
+     * A boolean informing the camera framework whether the HAL buffer manager
+     * must be used for the session configured.
+     */
+    boolean enableHalBufferManager = false;
+}
diff --git a/camera/device/aidl/android/hardware/camera/device/ICameraDevice.aidl b/camera/device/aidl/android/hardware/camera/device/ICameraDevice.aidl
index f940000..a2bdd59 100644
--- a/camera/device/aidl/android/hardware/camera/device/ICameraDevice.aidl
+++ b/camera/device/aidl/android/hardware/camera/device/ICameraDevice.aidl
@@ -21,6 +21,7 @@
 import android.hardware.camera.device.ICameraDeviceCallback;
 import android.hardware.camera.device.ICameraDeviceSession;
 import android.hardware.camera.device.ICameraInjectionSession;
+import android.hardware.camera.device.RequestTemplate;
 import android.hardware.camera.device.StreamConfiguration;
 import android.os.ParcelFileDescriptor;
 
@@ -346,4 +347,92 @@
      *
      */
     int getTorchStrengthLevel();
+
+    /**
+     * constructDefaultRequestSettings:
+     *
+     * This is the same as ICameraDeviceSession.constructDefaultRequestSettings, with
+     * the exception that it can be called before the ICameraDevice.open call.
+     *
+     * @param type The requested template CaptureRequest type to create the default settings for.
+     *
+     * @return capture settings for the requested use case.
+     *
+     */
+    CameraMetadata constructDefaultRequestSettings(in RequestTemplate type);
+
+    /**
+     * isStreamCombinationWithSettingsSupported:
+     *
+     * This is the same as isStreamCombinationSupported with below exceptions:
+     *
+     * 1. The input StreamConfiguration parameter may contain session parameters
+     * supported by this camera device. When checking if the particular StreamConfiguration
+     * is supported, the camera HAL must take the session parameters into consideration.
+     *
+     * 2. For version 3 of this interface, the camera compliance test will verify that
+     * isStreamCombinationWithSettingsSupported behaves properly for all combinations of
+     * below features. This function must return true for all supported combinations,
+     * and return false for non-supported feature combinations. The list of features
+     * required may grow in future versions.
+     *
+     * - Stream Combinations (a subset of LEGACY device mandatory stream combinations):
+     *   {
+     *     //                    4:3                16:9
+     *     // S1440P:         1920 x 1440         2560 x 1440
+     *     // S1080P:         1440 x 1080         1920 x 1080
+     *     // S720P:           960 x 720          1280 x 720
+     *
+     *     // Simple preview, GPU video processing, or no-preview video recording
+     *     {PRIV, MAXIMUM},
+     *     {PRIV, PREVIEW},
+     *     {PRIV, S1440P},
+     *     {PRIV, S1080P},
+     *     {PRIV, S720P},
+     *     // In-application video/image processing
+     *     {YUV, MAXIMUM},
+     *     {YUV, PREVIEW},
+     *     {YUV, S1440P},
+     *     {YUV, S1080P},
+     *     {YUV, S720P},
+     *     // Standard still imaging.
+     *     {PRIV, PREVIEW, JPEG, MAXIMUM},
+     *     {PRIV, S1440P,  JPEG, MAXIMUM},
+     *     {PRIV, S1080P,  JPEG, MAXIMUM},
+     *     {PRIV, S720P,   JPEG, MAXIMUM},
+     *     {PRIV, S1440P,  JPEG, S1440P},
+     *     {PRIV, S1080P,  JPEG, S1080P},
+     *     {PRIV, S720P,   JPEG, S1080P},
+     *     // In-app processing plus still capture.
+     *     {YUV,  PREVIEW, JPEG, MAXIMUM},
+     *     {YUV,  S1440P,  JPEG, MAXIMUM},
+     *     {YUV,  S1080P,  JPEG, MAXIMUM},
+     *     {YUV,  S720P,   JPEG, MAXIMUM},
+     *     // Standard recording.
+     *     {PRIV, PREVIEW, PRIV, PREVIEW},
+     *     {PRIV, S1440P,  PRIV, S1440P},
+     *     {PRIV, S1080P,  PRIV, S1080P},
+     *     {PRIV, S720P,   PRIV, S720P},
+     *     // Preview plus in-app processing.
+     *     {PRIV, PREVIEW, YUV,  PREVIEW},
+     *     {PRIV, S1440P,  YUV,  S1440P},
+     *     {PRIV, S1080P,  YUV,  S1080P},
+     *     {PRIV, S720P,   YUV,  S720P},
+     *   }
+     * - VIDEO_STABILIZATION_MODES: {OFF, PREVIEW}
+     * - AE_TARGET_FPS_RANGE: {{*, 30}, {*, 60}}
+     * - DYNAMIC_RANGE_PROFILE: {STANDARD, HLG10}
+     *
+     * Note: If a combination contains a S1440P, S1080P, or S720P stream,
+     * both 4:3 and 16:9 aspect ratio will be considered. For example, for the
+     * stream combination of {PRIV, S1440P, JPEG, MAXIMUM}, and if MAXIMUM ==
+     * 4032 x 3024, the camera compliance test will verify both
+     * {PRIV, 1920 x 1440, JPEG, 4032 x 3024} and {PRIV, 2560 x 1440, JPEG, 4032 x 2268}.
+     *
+     * @param streams The StreamConfiguration to be tested, with optional session parameters.
+     *
+     * @return true in case the stream combination is supported, false otherwise.
+     *
+     */
+    boolean isStreamCombinationWithSettingsSupported(in StreamConfiguration streams);
 }
diff --git a/camera/device/aidl/android/hardware/camera/device/ICameraDeviceSession.aidl b/camera/device/aidl/android/hardware/camera/device/ICameraDeviceSession.aidl
index 885c71a..ffc1a11 100644
--- a/camera/device/aidl/android/hardware/camera/device/ICameraDeviceSession.aidl
+++ b/camera/device/aidl/android/hardware/camera/device/ICameraDeviceSession.aidl
@@ -20,6 +20,7 @@
 import android.hardware.camera.device.CameraMetadata;
 import android.hardware.camera.device.CameraOfflineSessionInfo;
 import android.hardware.camera.device.CaptureRequest;
+import android.hardware.camera.device.ConfigureStreamsRet;
 import android.hardware.camera.device.HalStream;
 import android.hardware.camera.device.ICameraOfflineSession;
 import android.hardware.camera.device.RequestTemplate;
@@ -88,6 +89,13 @@
      * with processCaptureResult (and its respective releaseFence has been
      * signaled) the framework may free or reuse it at any time.
      *
+     * This method wil only be called by the framework if
+     * ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION is either not advertised or is
+     * ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_AIDL. If the value of
+     * ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION is
+     * ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_SESSION_CONFIGURABLE, configureStreamsV2
+     * will be called instead.
+     *
      * ------------------------------------------------------------------------
      *
      * Preconditions:
@@ -386,8 +394,8 @@
      *          error.
      * @return true in case the stream reconfiguration is required, false otherwise.
      */
-    boolean isReconfigurationRequired(in CameraMetadata oldSessionParams,
-                                      in CameraMetadata newSessionParams);
+    boolean isReconfigurationRequired(
+            in CameraMetadata oldSessionParams, in CameraMetadata newSessionParams);
 
     /**
      * processCaptureRequest:
@@ -576,4 +584,29 @@
      */
     void repeatingRequestEnd(in int frameNumber, in int[] streamIds);
 
+    /**
+     *
+     * configureStreamsV2:
+     *
+     * Performs the same function as 'configureStreams'. This function returns a
+     * 'ConfigureStreamsRet' Parcelable. This tells the framework about the desired stream
+     * parameters such as usage flags, maximum buffers, overridden format etc. It also informs
+     * camera framework whether the HAL will use the HAL buffer manager APIs 'requestStreamBuffers'
+     * and 'returnStreamBuffers' to request and return buffers to the framework.
+     *
+     * This method is only supported if
+     * ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION is
+     * ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_SESSION_CONFIGURABLE. It must not be
+     * called by the camera framework if it isn't supported. That is, the framework will only
+     * ever call one of 'configureStreams' or 'configureStreamsV2' depending on the value of
+     * ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION.
+     *
+     * @param requestedConfiguration The stream configuration requested by the camera framework to
+     *        be configured by the camera HAL.
+     * @return A ConfigureStreamsRet Parcelable containing a vector of HalStreams and a boolean
+     *         specifying whether the HAL buffer manager must be used for this session
+     *         configuration.
+     *
+     */
+    ConfigureStreamsRet configureStreamsV2(in StreamConfiguration requestedConfiguration);
 }
diff --git a/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataTag.aidl b/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataTag.aidl
index 0290aef..b04e486 100644
--- a/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataTag.aidl
+++ b/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataTag.aidl
@@ -283,6 +283,8 @@
   ANDROID_STATISTICS_OIS_TIMESTAMPS,
   ANDROID_STATISTICS_OIS_X_SHIFTS,
   ANDROID_STATISTICS_OIS_Y_SHIFTS,
+  ANDROID_STATISTICS_LENS_INTRINSIC_TIMESTAMPS,
+  ANDROID_STATISTICS_LENS_INTRINSIC_SAMPLES,
   ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES = android.hardware.camera.metadata.CameraMetadataSectionStart.ANDROID_STATISTICS_INFO_START /* 1179648 */,
   ANDROID_STATISTICS_INFO_HISTOGRAM_BUCKET_COUNT,
   ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
@@ -329,6 +331,7 @@
   ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS = android.hardware.camera.metadata.CameraMetadataSectionStart.ANDROID_LOGICAL_MULTI_CAMERA_START /* 1703936 */,
   ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE,
   ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID,
+  ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_SENSOR_CROP_REGION,
   ANDROID_DISTORTION_CORRECTION_MODE = android.hardware.camera.metadata.CameraMetadataSectionStart.ANDROID_DISTORTION_CORRECTION_START /* 1769472 */,
   ANDROID_DISTORTION_CORRECTION_AVAILABLE_MODES,
   ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS = android.hardware.camera.metadata.CameraMetadataSectionStart.ANDROID_HEIC_START /* 1835008 */,
diff --git a/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/InfoSupportedBufferManagementVersion.aidl b/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/InfoSupportedBufferManagementVersion.aidl
index 7303ff5..2c31cff 100644
--- a/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/InfoSupportedBufferManagementVersion.aidl
+++ b/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/InfoSupportedBufferManagementVersion.aidl
@@ -39,4 +39,5 @@
 @Backing(type="int") @VintfStability
 enum InfoSupportedBufferManagementVersion {
   ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_AIDL_DEVICE,
+  ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_SESSION_CONFIGURABLE,
 }
diff --git a/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataTag.aidl b/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataTag.aidl
index cfc9907..2fefce0 100644
--- a/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataTag.aidl
+++ b/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataTag.aidl
@@ -1868,6 +1868,18 @@
      */
     ANDROID_STATISTICS_OIS_Y_SHIFTS,
     /**
+     * android.statistics.lensIntrinsicTimestamps [dynamic, int64[], ndk_public]
+     *
+     * <p>An array of timestamps of lens intrinsics samples, in nanoseconds.</p>
+     */
+    ANDROID_STATISTICS_LENS_INTRINSIC_TIMESTAMPS,
+    /**
+     * android.statistics.lensIntrinsicSamples [dynamic, float[], ndk_public]
+     *
+     * <p>An array of intra-frame lens intrinsics.</p>
+     */
+    ANDROID_STATISTICS_LENS_INTRINSIC_SAMPLES,
+    /**
      * android.statistics.info.availableFaceDetectModes [static, byte[], public]
      *
      * <p>List of face detection modes for ANDROID_STATISTICS_FACE_DETECT_MODE that are
@@ -2241,6 +2253,13 @@
      */
     ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID,
     /**
+     * android.logicalMultiCamera.activePhysicalSensorCropRegion [dynamic, int32[], public]
+     *
+     * <p>The current region of the active physical sensor that will be read out for this
+     * capture.</p>
+     */
+    ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_SENSOR_CROP_REGION,
+    /**
      * android.distortionCorrection.mode [dynamic, enum, public]
      *
      * <p>Mode of operation for the lens distortion correction block.</p>
diff --git a/camera/metadata/aidl/android/hardware/camera/metadata/InfoSupportedBufferManagementVersion.aidl b/camera/metadata/aidl/android/hardware/camera/metadata/InfoSupportedBufferManagementVersion.aidl
index 9522377..964d079 100644
--- a/camera/metadata/aidl/android/hardware/camera/metadata/InfoSupportedBufferManagementVersion.aidl
+++ b/camera/metadata/aidl/android/hardware/camera/metadata/InfoSupportedBufferManagementVersion.aidl
@@ -30,4 +30,5 @@
 @Backing(type="int")
 enum InfoSupportedBufferManagementVersion {
     ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_AIDL_DEVICE,
+    ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_SESSION_CONFIGURABLE,
 }
diff --git a/camera/provider/aidl/Android.bp b/camera/provider/aidl/Android.bp
index 35ec976..f6b00a1 100644
--- a/camera/provider/aidl/Android.bp
+++ b/camera/provider/aidl/Android.bp
@@ -14,10 +14,10 @@
         "android/hardware/camera/provider/*.aidl",
     ],
     imports: [
-        "android.hardware.camera.device-V2",
+        "android.hardware.camera.device-V3",
         "android.hardware.camera.common-V1",
     ],
-    frozen: true,
+    frozen: false,
     stability: "vintf",
     backend: {
         java: {
diff --git a/camera/provider/aidl/vts/Android.bp b/camera/provider/aidl/vts/Android.bp
index cfcaec5..f9305bb 100644
--- a/camera/provider/aidl/vts/Android.bp
+++ b/camera/provider/aidl/vts/Android.bp
@@ -60,9 +60,9 @@
     static_libs: [
         "android.hardware.camera.common@1.0-helper",
         "android.hardware.camera.common-V1-ndk",
-        "android.hardware.camera.device-V2-ndk",
+        "android.hardware.camera.device-V3-ndk",
         "android.hardware.camera.metadata-V2-ndk",
-        "android.hardware.camera.provider-V2-ndk",
+        "android.hardware.camera.provider-V3-ndk",
         "android.hidl.allocator@1.0",
         "libgrallocusage",
         "libhidlmemory",
diff --git a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
index def6233..e335853 100644
--- a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
+++ b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
@@ -500,6 +500,12 @@
         ASSERT_TRUE(ret.isOk());
         ASSERT_NE(device, nullptr);
 
+        int32_t interfaceVersion;
+        ret = device->getInterfaceVersion(&interfaceVersion);
+        ASSERT_TRUE(ret.isOk());
+        bool supportFeatureCombinationQuery =
+                (interfaceVersion >= CAMERA_DEVICE_API_MINOR_VERSION_3);
+
         std::shared_ptr<EmptyDeviceCb> cb = ndk::SharedRefBase::make<EmptyDeviceCb>();
         ret = device->open(cb, &mSession);
         ALOGI("device::open returns status:%d:%d", ret.getExceptionCode(),
@@ -533,6 +539,34 @@
             } else {
                 ASSERT_EQ(0u, rawMetadata.metadata.size());
             }
+
+            if (flags::feature_combination_query()) {
+                if (supportFeatureCombinationQuery) {
+                    CameraMetadata rawMetadata2;
+                    ndk::ScopedAStatus ret2 =
+                            device->constructDefaultRequestSettings(reqTemplate, &rawMetadata2);
+
+                    // TODO: Do not allow OPERATION_NOT_SUPPORTED once HAL
+                    // implementation is in place.
+                    if (static_cast<Status>(ret2.getServiceSpecificError()) !=
+                        Status::OPERATION_NOT_SUPPORTED) {
+                        ASSERT_EQ(ret.isOk(), ret2.isOk());
+                        ASSERT_EQ(ret.getStatus(), ret2.getStatus());
+
+                        ASSERT_EQ(rawMetadata.metadata.size(), rawMetadata2.metadata.size());
+                        if (ret2.isOk()) {
+                            const camera_metadata_t* metadata =
+                                    (camera_metadata_t*)rawMetadata2.metadata.data();
+                            size_t expectedSize = rawMetadata2.metadata.size();
+                            int result =
+                                    validate_camera_metadata_structure(metadata, &expectedSize);
+                            ASSERT_TRUE((result == 0) ||
+                                        (result == CAMERA_METADATA_VALIDATION_SHIFTED));
+                            verifyRequestTemplate(metadata, reqTemplate);
+                        }
+                    }
+                }
+            }
         }
         ret = mSession->close();
         mSession = nullptr;
@@ -588,8 +622,7 @@
             createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config,
                                       jpegBufferSize);
 
-            bool expectStreamCombQuery = (isLogicalMultiCamera(staticMeta) == Status::OK);
-            verifyStreamCombination(device, config, /*expectedStatus*/ true, expectStreamCombQuery);
+            verifyStreamCombination(device, config, /*expectedStatus*/ true);
 
             config.streamConfigCounter = streamConfigCounter++;
             std::vector<HalStream> halConfigs;
@@ -687,11 +720,7 @@
         // Test the stream can actually be configured
         for (auto& cti : cameraTestInfos) {
             if (cti.session != nullptr) {
-                camera_metadata_t* staticMeta =
-                        reinterpret_cast<camera_metadata_t*>(cti.staticMeta.metadata.data());
-                bool expectStreamCombQuery = (isLogicalMultiCamera(staticMeta) == Status::OK);
-                verifyStreamCombination(cti.cameraDevice, cti.config, /*expectedStatus*/ true,
-                                        expectStreamCombQuery);
+                verifyStreamCombination(cti.cameraDevice, cti.config, /*expectedStatus*/ true);
             }
 
             if (cti.session != nullptr) {
@@ -752,8 +781,7 @@
         createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config,
                                   jpegBufferSize);
 
-        verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ false,
-                                /*expectStreamCombQuery*/ false);
+        verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ false);
 
         config.streamConfigCounter = streamConfigCounter++;
         std::vector<HalStream> halConfigs;
@@ -972,8 +1000,7 @@
                 createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config,
                                           jpegBufferSize);
 
-                verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true,
-                                        /*expectStreamCombQuery*/ false);
+                verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true);
 
                 config.streamConfigCounter = streamConfigCounter++;
                 std::vector<HalStream> halConfigs;
@@ -1191,8 +1218,7 @@
                 createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config,
                                           jpegBufferSize);
                 config.streamConfigCounter = streamConfigCounter++;
-                verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true,
-                                        /*expectStreamCombQuery*/ false);
+                verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true);
 
                 std::vector<HalStream> halConfigs;
                 ndk::ScopedAStatus ret = mSession->configureStreams(config, &halConfigs);
@@ -1256,8 +1282,7 @@
         createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
                                   &config);
 
-        verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true,
-                                /*expectStreamCombQuery*/ false);
+        verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true);
 
         config.streamConfigCounter = streamConfigCounter++;
         std::vector<HalStream> halConfigs;
@@ -1429,8 +1454,7 @@
 
                 createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config,
                                           jpegBufferSize);
-                verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true,
-                                        /*expectStreamCombQuery*/ false);
+                verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true);
 
                 config.streamConfigCounter = streamConfigCounter++;
                 std::vector<HalStream> halConfigs;
diff --git a/camera/provider/aidl/vts/camera_aidl_test.cpp b/camera/provider/aidl/vts/camera_aidl_test.cpp
index e5bf637..8e72b3f 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.cpp
+++ b/camera/provider/aidl/vts/camera_aidl_test.cpp
@@ -30,6 +30,7 @@
 #include <aidlcommonsupport/NativeHandle.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
+#include <com_android_internal_camera_flags.h>
 #include <device_cb.h>
 #include <empty_device_cb.h>
 #include <grallocusage/GrallocUsageConversion.h>
@@ -39,6 +40,7 @@
 #include <ui/GraphicBufferAllocator.h>
 #include <regex>
 #include <typeinfo>
+#include "utils/Errors.h"
 
 using ::aidl::android::hardware::camera::common::CameraDeviceStatus;
 using ::aidl::android::hardware::camera::common::TorchModeStatus;
@@ -56,6 +58,8 @@
 using ::ndk::SpAIBinder;
 
 namespace {
+namespace flags = com::android::internal::camera::flags;
+
 bool parseProviderName(const std::string& serviceDescriptor, std::string* type /*out*/,
                        uint32_t* id /*out*/) {
     if (!type || !id) {
@@ -101,6 +105,8 @@
     return true;
 }
 
+namespace flags = com::android::internal::camera::flags;
+
 const std::vector<int64_t> kMandatoryUseCases = {
         ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT,
         ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW,
@@ -478,6 +484,38 @@
     } else {
         ADD_FAILURE() << "Get LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID failed!";
     }
+
+    if (flags::concert_mode()) {
+        auto ret = find_camera_metadata_ro_entry(
+                metadata, ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_SENSOR_CROP_REGION, &entry);
+        if ((ret == android::OK) && (entry.count > 0)) {
+            ASSERT_TRUE(entry.count == 4);
+            ASSERT_GE(entry.data.i32[0], 0);  // Top must be non-negative
+            ASSERT_GE(entry.data.i32[1], 0);  // Left must be non-negative
+            ASSERT_GT(entry.data.i32[2], 0);  // Width must be positive
+            ASSERT_GT(entry.data.i32[3], 0);  // Height must be positive
+        }
+    }
+}
+
+void CameraAidlTest::verifyLensIntrinsicsResult(const std::vector<uint8_t>& resultMetadata) {
+    if (flags::concert_mode()) {
+        camera_metadata_t* metadata = (camera_metadata_t*)resultMetadata.data();
+
+        camera_metadata_ro_entry timestampsEntry, intrinsicsEntry;
+        auto tsRet = find_camera_metadata_ro_entry(
+                metadata, ANDROID_STATISTICS_LENS_INTRINSIC_TIMESTAMPS, &timestampsEntry);
+        auto inRet = find_camera_metadata_ro_entry(
+                metadata, ANDROID_STATISTICS_LENS_INTRINSIC_SAMPLES, &intrinsicsEntry);
+        ASSERT_EQ(tsRet, inRet);
+        ASSERT_TRUE((intrinsicsEntry.count % 5) == 0);
+        ASSERT_EQ(timestampsEntry.count, intrinsicsEntry.count / 5);
+        if (timestampsEntry.count > 0) {
+            for (size_t i = 0; i < timestampsEntry.count - 1; i++) {
+                ASSERT_GE(timestampsEntry.data.i64[i + 1], timestampsEntry.data.i64[i]);
+            }
+        }
+    }
 }
 
 Status CameraAidlTest::getPhysicalCameraIds(const camera_metadata_t* staticMeta,
@@ -1350,13 +1388,17 @@
     bool hasHalBufferManager =
             (0 == retcode && 1 == entry.count &&
              entry.data.i32[0] == ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
+    bool sessionHalBufferManager =
+            (0 == retcode && 1 == entry.count &&
+             entry.data.i32[0] ==
+                     ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_SESSION_CONFIGURABLE);
     retcode = find_camera_metadata_ro_entry(
             metadata, ANDROID_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED, &entry);
     bool multiResolutionStreamSupported =
             (0 == retcode && 1 == entry.count &&
              entry.data.u8[0] == ANDROID_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED_TRUE);
     if (multiResolutionStreamSupported) {
-        ASSERT_TRUE(hasHalBufferManager);
+        ASSERT_TRUE(hasHalBufferManager || sessionHalBufferManager);
     }
 
     std::string version, cameraId;
@@ -1859,17 +1901,32 @@
 }
 
 void CameraAidlTest::verifyStreamCombination(const std::shared_ptr<ICameraDevice>& device,
-                                             const StreamConfiguration& config, bool expectedStatus,
-                                             bool expectStreamCombQuery) {
+                                             const StreamConfiguration& config,
+                                             bool expectedStatus) {
     if (device != nullptr) {
         bool streamCombinationSupported;
         ScopedAStatus ret =
                 device->isStreamCombinationSupported(config, &streamCombinationSupported);
-        // TODO: Check is unsupported operation is correct.
-        ASSERT_TRUE(ret.isOk() ||
-                    (expectStreamCombQuery && ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION));
-        if (ret.isOk()) {
-            ASSERT_EQ(expectedStatus, streamCombinationSupported);
+        ASSERT_TRUE(ret.isOk());
+        ASSERT_EQ(expectedStatus, streamCombinationSupported);
+
+        if (flags::feature_combination_query()) {
+            int32_t interfaceVersion;
+            ret = device->getInterfaceVersion(&interfaceVersion);
+            ASSERT_TRUE(ret.isOk());
+            bool supportFeatureCombinationQuery =
+                    (interfaceVersion >= CAMERA_DEVICE_API_MINOR_VERSION_3);
+            if (supportFeatureCombinationQuery) {
+                ret = device->isStreamCombinationWithSettingsSupported(config,
+                                                                       &streamCombinationSupported);
+                // TODO: Do not allow OPERATION_NOT_SUPPORTED once HAL
+                // implementation is in place.
+                ASSERT_TRUE(ret.isOk() || static_cast<Status>(ret.getServiceSpecificError()) ==
+                                                  Status::OPERATION_NOT_SUPPORTED);
+                if (ret.isOk()) {
+                    ASSERT_EQ(expectedStatus, streamCombinationSupported);
+                }
+            }
         }
     }
 }
@@ -2440,6 +2497,28 @@
 
 }
 
+ndk::ScopedAStatus CameraAidlTest::configureStreams(std::shared_ptr<ICameraDeviceSession>& session,
+                                                    const StreamConfiguration& config,
+                                                    bool sessionHalBufferManager,
+                                                    bool* useHalBufManager,
+                                                    std::vector<HalStream>* halStreams) {
+    auto ret = ndk::ScopedAStatus::ok();
+    ConfigureStreamsRet aidl_return;
+    if (sessionHalBufferManager) {
+        ret = session->configureStreamsV2(config, &aidl_return);
+    } else {
+        ret = session->configureStreams(config, halStreams);
+    }
+    if (!ret.isOk()) {
+        return ret;
+    }
+    if (sessionHalBufferManager) {
+        *useHalBufManager = aidl_return.enableHalBufferManager;
+        *halStreams = std::move(aidl_return.halStreams);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
 void CameraAidlTest::configureSingleStream(
         const std::string& name, const std::shared_ptr<ICameraProvider>& provider,
         const AvailableStream* previewThreshold, uint64_t bufferUsage, RequestTemplate reqTemplate,
@@ -2494,11 +2573,15 @@
     ASSERT_NE(*session, nullptr);
 
     *useHalBufManager = false;
+    bool sessionHalBufferManager = false;
     status = find_camera_metadata_ro_entry(
             staticMeta, ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, &entry);
     if ((0 == status) && (entry.count == 1)) {
         *useHalBufManager = (entry.data.u8[0] ==
                              ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
+        sessionHalBufferManager =
+                (entry.data.u8[0] ==
+                 ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_SESSION_CONFIGURABLE);
     }
 
     outputPreviewStreams.clear();
@@ -2557,7 +2640,8 @@
         ASSERT_EQ(supported, true);
 
         std::vector<HalStream> halConfigs;
-        ret = (*session)->configureStreams(config, &halConfigs);
+        ret = configureStreams(*session, config, sessionHalBufferManager, useHalBufManager,
+                               &halConfigs);
         ALOGI("configureStreams returns status: %d:%d", ret.getExceptionCode(),
               ret.getServiceSpecificError());
         ASSERT_TRUE(ret.isOk());
@@ -2853,11 +2937,15 @@
     ASSERT_NE(*session, nullptr);
 
     *useHalBufManager = false;
+    bool sessionHalBufferManager = false;
     status = find_camera_metadata_ro_entry(
             staticMeta, ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, &entry);
     if ((0 == status) && (entry.count == 1)) {
         *useHalBufManager = (entry.data.u8[0] ==
                              ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
+        sessionHalBufferManager =
+                (entry.data.u8[0] ==
+                 ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_SESSION_CONFIGURABLE);
     }
 
     outputPreviewStreams.clear();
@@ -2912,7 +3000,9 @@
 
     config.streamConfigCounter = streamConfigCounter;
     std::vector<HalStream> halConfigs;
-    ret = (*session)->configureStreams(config, &halConfigs);
+    ret = configureStreams(*session, config, sessionHalBufferManager, useHalBufManager,
+                           &halConfigs);
+
     ASSERT_TRUE(ret.isOk());
     ASSERT_EQ(physicalIds.size(), halConfigs.size());
     *halStreams = halConfigs;
@@ -2992,11 +3082,15 @@
     ASSERT_NE(*session, nullptr);
 
     *useHalBufManager = false;
+    bool sessionHalBufferManager = false;
     status = find_camera_metadata_ro_entry(
             staticMeta, ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, &entry);
     if ((0 == status) && (entry.count == 1)) {
         *useHalBufManager = (entry.data.u8[0] ==
                              ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
+        sessionHalBufferManager =
+                (entry.data.u8[0] ==
+                 ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_SESSION_CONFIGURABLE);
     }
 
     outputStreams.clear();
@@ -3050,7 +3144,8 @@
     ASSERT_TRUE(ret.isOk());
     ASSERT_EQ(supported, true);
 
-    ret = (*session)->configureStreams(config, halStreams);
+    ret = configureStreams(*session, config, sessionHalBufferManager, useHalBufManager, halStreams);
+
     ASSERT_TRUE(ret.isOk());
 
     if (*useHalBufManager) {
@@ -3434,11 +3529,15 @@
     }
 
     *useHalBufManager = false;
+    bool sessionHalBufferManager = false;
     status = find_camera_metadata_ro_entry(
             staticMeta, ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, &entry);
     if ((0 == status) && (entry.count == 1)) {
         *useHalBufManager = (entry.data.u8[0] ==
                              ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
+        sessionHalBufferManager =
+                (entry.data.u8[0] ==
+                 ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_SESSION_CONFIGURABLE);
     }
 
     auto st = getJpegBufferSize(staticMeta, jpegBufferSize);
@@ -3491,7 +3590,8 @@
 
     StreamConfiguration config = {streams, StreamConfigurationMode::NORMAL_MODE, CameraMetadata()};
 
-    (*session)->configureStreams(config, halStreams);
+    ret = configureStreams(*session, config, sessionHalBufferManager, useHalBufManager, halStreams);
+
     ASSERT_TRUE(ret.isOk());
 
     if (*useHalBufManager) {
diff --git a/camera/provider/aidl/vts/camera_aidl_test.h b/camera/provider/aidl/vts/camera_aidl_test.h
index 0ebd4ef..b51544f 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.h
+++ b/camera/provider/aidl/vts/camera_aidl_test.h
@@ -64,6 +64,7 @@
 using ::aidl::android::hardware::camera::device::BufferRequestStatus;
 using ::aidl::android::hardware::camera::device::CameraMetadata;
 using ::aidl::android::hardware::camera::device::CaptureResult;
+using ::aidl::android::hardware::camera::device::ConfigureStreamsRet;
 using ::aidl::android::hardware::camera::device::ErrorCode;
 using ::aidl::android::hardware::camera::device::HalStream;
 using ::aidl::android::hardware::camera::device::ICameraDevice;
@@ -201,6 +202,11 @@
             int32_t* partialResultCount /*out*/, std::shared_ptr<DeviceCb>* outCb /*out*/,
             int32_t* jpegBufferSize /*out*/, bool* useHalBufManager /*out*/);
 
+    ndk::ScopedAStatus configureStreams(std::shared_ptr<ICameraDeviceSession>& session,
+                                        const StreamConfiguration& config,
+                                        bool sessionHalBufferManager, bool* useHalBufManager,
+                                        std::vector<HalStream>* halStreams);
+
     void configureStreams(
             const std::string& name, const std::shared_ptr<ICameraProvider>& provider,
             PixelFormat format, std::shared_ptr<ICameraDeviceSession>* session /*out*/,
@@ -273,12 +279,13 @@
     static void verifySettingsOverrideCharacteristics(const camera_metadata_t* metadata);
 
     static void verifyStreamCombination(const std::shared_ptr<ICameraDevice>& device,
-                                        const StreamConfiguration& config, bool expectedStatus,
-                                        bool expectStreamCombQuery);
+                                        const StreamConfiguration& config, bool expectedStatus);
 
     static void verifyLogicalCameraResult(const camera_metadata_t* staticMetadata,
                                           const std::vector<uint8_t>& resultMetadata);
 
+    static void verifyLensIntrinsicsResult(const std::vector<uint8_t>& resultMetadata);
+
     static void verifyBuffersReturned(const std::shared_ptr<ICameraDeviceSession>& session,
                                       int32_t streamId, const std::shared_ptr<DeviceCb>& cb,
                                       uint32_t streamConfigCounter = 0);
@@ -617,6 +624,7 @@
 // device@<major>.<minor>/<type>/id
 const char* kDeviceNameRE = "device@([0-9]+\\.[0-9]+)/\\s+/(.+)";
 const std::string CAMERA_DEVICE_API_VERSION_1 = "1.1";
+const int32_t CAMERA_DEVICE_API_MINOR_VERSION_3 = 3;
 
 const int32_t kMaxVideoWidth = 4096;
 const int32_t kMaxVideoHeight = 2160;
diff --git a/camera/provider/aidl/vts/device_cb.cpp b/camera/provider/aidl/vts/device_cb.cpp
index 7e0969a..2c11d3f 100644
--- a/camera/provider/aidl/vts/device_cb.cpp
+++ b/camera/provider/aidl/vts/device_cb.cpp
@@ -388,15 +388,16 @@
         // Verify logical camera result metadata
         bool isLogicalCamera =
                 Status::OK == CameraAidlTest::isLogicalMultiCamera(staticMetadataBuffer);
+        camera_metadata_t* collectedMetadata =
+                const_cast<camera_metadata_t*>(request->collectedResult.getAndLock());
+        uint8_t* rawMetadata = reinterpret_cast<uint8_t*>(collectedMetadata);
+        std::vector metadata =
+                std::vector(rawMetadata, rawMetadata + get_camera_metadata_size(collectedMetadata));
         if (isLogicalCamera) {
-            camera_metadata_t* collectedMetadata =
-                    const_cast<camera_metadata_t*>(request->collectedResult.getAndLock());
-            uint8_t* rawMetadata = reinterpret_cast<uint8_t*>(collectedMetadata);
-            std::vector metadata = std::vector(
-                    rawMetadata, rawMetadata + get_camera_metadata_size(collectedMetadata));
             CameraAidlTest::verifyLogicalCameraResult(staticMetadataBuffer, metadata);
-            request->collectedResult.unlock(collectedMetadata);
         }
+        CameraAidlTest::verifyLensIntrinsicsResult(metadata);
+        request->collectedResult.unlock(collectedMetadata);
     }
 
     uint32_t numBuffersReturned = results.outputBuffers.size();
diff --git a/compatibility_matrices/Android.bp b/compatibility_matrices/Android.bp
index b3ca293..9bee3b9 100644
--- a/compatibility_matrices/Android.bp
+++ b/compatibility_matrices/Android.bp
@@ -90,7 +90,7 @@
         "compatibility_matrix.9.xml",
     ],
     kernel_configs: [
-        "kernel_config_v_5.15",
         "kernel_config_v_6.1",
+        "kernel_config_v_6.6",
     ],
 }
diff --git a/compatibility_matrices/compatibility_matrix.9.xml b/compatibility_matrices/compatibility_matrix.9.xml
index 2f33a28..33e5a7d 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>
@@ -173,7 +181,7 @@
     </hal>
     <hal format="aidl" optional="true" updatable-via-apex="true">
         <name>android.hardware.camera.provider</name>
-        <version>1-2</version>
+        <version>1-3</version>
         <interface>
             <name>ICameraProvider</name>
             <regex-instance>[^/]+/[0-9]+</regex-instance>
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 7e62f5e..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
@@ -44,5 +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);
-  oneway void onHotplugEvent(long display, android.hardware.graphics.common.DisplayHotplugEvent event);
+  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 5df140d..96eccd7 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerCallback.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerCallback.aidl
@@ -138,5 +138,5 @@
      * @param display is the display that triggers the hotplug event.
      * @param event is the type of event that occurred.
      */
-    oneway void onHotplugEvent(long display, DisplayHotplugEvent event);
+    void onHotplugEvent(long display, DisplayHotplugEvent event);
 }
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/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/radio/aidl/vts/radio_network_test.cpp b/radio/aidl/vts/radio_network_test.cpp
index 98422af..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) {
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/NanDataPathSecurityConfig.aidl b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/NanDataPathSecurityConfig.aidl
index 635dbce..48e9501 100644
--- a/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/NanDataPathSecurityConfig.aidl
+++ b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/NanDataPathSecurityConfig.aidl
@@ -39,4 +39,10 @@
   byte[32] pmk;
   byte[] passphrase;
   byte[16] scid;
+  boolean enable16ReplyCountersForTksa;
+  boolean enable16ReplyCountersForGtksa;
+  boolean supportGtkAndIgtk;
+  boolean supportBigtksa;
+  boolean enableNcsBip256;
+  boolean requiresEnhancedFrameProtection;
 }
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/NanDataPathSecurityConfig.aidl b/wifi/aidl/android/hardware/wifi/NanDataPathSecurityConfig.aidl
index 9a2013b..b6c5eef 100644
--- a/wifi/aidl/android/hardware/wifi/NanDataPathSecurityConfig.aidl
+++ b/wifi/aidl/android/hardware/wifi/NanDataPathSecurityConfig.aidl
@@ -58,4 +58,49 @@
      * setting up the Secure Data Path.
      */
     byte[16] scid;
+
+    /**
+     * Enables the 16 replay counter for ND-TKSA(NAN Data Pairwise Security Association) and
+     * NM-TKSA(NAN managerment Pairwise Security Association), if set to false will use 4 replay
+     * counter as default
+     * Wi-Fi Aware spec 4.0: 9.5.21.2 Cipher Suite Information attribute
+     */
+    boolean enable16ReplyCountersForTksa;
+
+    /**
+     * Enables the 16 replay counter for GTKSA(Group Transient Key security associations), if set to
+     * false will use 4 replay counter as default.
+     * Wi-Fi Aware spec 4.0: 9.5.21.2 Cipher Suite Information attribute
+     */
+    boolean enable16ReplyCountersForGtksa;
+
+    /**
+     * GTK(Group Transient Key) used to protect group addressed data frames,
+     * IGTK(Integrity Group Transient Key) used to protect multicast management frames, set to true
+     * if supported.
+     * Wi-Fi Aware spec 4.0: 9.5.21.2 Cipher Suite Information attribute
+     */
+    boolean supportGtkAndIgtk;
+
+    /**
+     * BIGTK(Beacon Integrity Group Transient Key) used to protect Beacon frames, set to true if
+     * supported.
+     * Ref: Wi-Fi Aware spec 4.0: 9.5.21.2 Cipher Suite Information attribute
+     */
+    boolean supportBigtksa;
+
+    /**
+     * Enables NCS-BIP-256 for IGTKSA(Integrity Group Transient Key security associations)
+     * and BIGTK(Beacon Integrity Group Transient Key security associations), if set to false will
+     * use NCS-BIP-128 as default
+     * Wi-Fi Aware spec 4.0: 9.5.21.2 Cipher Suite Information attribute
+     */
+    boolean enableNcsBip256;
+
+    /**
+     * Require enhanced frame protection if supported, which includes multicast management frame
+     * protection, group addressed data protection and beacon frame protection.
+     * Wi-Fi Aware spec 4.0: 7.3 frame protection
+     */
+    boolean requiresEnhancedFrameProtection;
 }
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..99420bd 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);
 }
@@ -2088,6 +2090,17 @@
     memcpy(legacy_request->scid, aidl_request.securityConfig.scid.data(), legacy_request->scid_len);
     legacy_request->publish_subscribe_id = static_cast<uint8_t>(aidl_request.discoverySessionId);
 
+    legacy_request->csia_capabilities |=
+            aidl_request.securityConfig.enable16ReplyCountersForTksa ? 0x1 : 0x0;
+    legacy_request->csia_capabilities |=
+            aidl_request.securityConfig.enable16ReplyCountersForGtksa ? 0x8 : 0x0;
+    if (aidl_request.securityConfig.supportGtkAndIgtk) {
+        legacy_request->csia_capabilities |= aidl_request.securityConfig.supportBigtksa ? 0x4 : 0x2;
+    }
+    legacy_request->csia_capabilities |= aidl_request.securityConfig.enableNcsBip256 ? 0x16 : 0x0;
+    legacy_request->gtk_protection =
+            aidl_request.securityConfig.requiresEnhancedFrameProtection ? 1 : 0;
+
     return true;
 }
 
@@ -2170,6 +2183,17 @@
     memcpy(legacy_request->scid, aidl_request.securityConfig.scid.data(), legacy_request->scid_len);
     legacy_request->publish_subscribe_id = static_cast<uint8_t>(aidl_request.discoverySessionId);
 
+    legacy_request->csia_capabilities |=
+            aidl_request.securityConfig.enable16ReplyCountersForTksa ? 0x1 : 0x0;
+    legacy_request->csia_capabilities |=
+            aidl_request.securityConfig.enable16ReplyCountersForGtksa ? 0x8 : 0x0;
+    if (aidl_request.securityConfig.supportGtkAndIgtk) {
+        legacy_request->csia_capabilities |= aidl_request.securityConfig.supportBigtksa ? 0x4 : 0x2;
+    }
+    legacy_request->csia_capabilities |= aidl_request.securityConfig.enableNcsBip256 ? 0x16 : 0x0;
+    legacy_request->gtk_protection =
+            aidl_request.securityConfig.requiresEnhancedFrameProtection ? 1 : 0;
+
     return true;
 }
 
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/supplicant/aidl/Android.bp b/wifi/supplicant/aidl/Android.bp
index 76b0902..632927d 100644
--- a/wifi/supplicant/aidl/Android.bp
+++ b/wifi/supplicant/aidl/Android.bp
@@ -27,6 +27,9 @@
     srcs: [
         "android/hardware/wifi/supplicant/*.aidl",
     ],
+    imports: [
+        "android.hardware.wifi.common-V1",
+    ],
     stability: "vintf",
     backend: {
         java: {
@@ -45,6 +48,9 @@
         ndk: {
             gen_trace: true,
         },
+        cpp: {
+            enabled: false,
+        },
     },
     versions_with_info: [
         {
@@ -58,5 +64,4 @@
 
     ],
     frozen: false,
-
 }