Merge "threadnetwork: Avoid execute command if service does not exist" into main
diff --git a/audio/aidl/TEST_MAPPING b/audio/aidl/TEST_MAPPING
index e325001..26f4b3a 100644
--- a/audio/aidl/TEST_MAPPING
+++ b/audio/aidl/TEST_MAPPING
@@ -72,12 +72,7 @@
       "name": "audiosystem_tests"
     },
     {
-      "name": "CtsVirtualDevicesTestCases",
-      "options" : [
-        {
-          "include-filter": "android.virtualdevice.cts.VirtualAudioTest"
-        }
-      ]
+      "name": "CtsVirtualDevicesAudioTestCases"
     }
   ]
 }
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index f51f65e..73d7626 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -62,7 +62,6 @@
         "ModulePrimary.cpp",
         "SoundDose.cpp",
         "Stream.cpp",
-        "StreamSwitcher.cpp",
         "Telephony.cpp",
         "XsdcConversion.cpp",
         "alsa/Mixer.cpp",
@@ -72,11 +71,13 @@
         "bluetooth/DevicePortProxy.cpp",
         "bluetooth/ModuleBluetooth.cpp",
         "bluetooth/StreamBluetooth.cpp",
+        "deprecated/StreamSwitcher.cpp",
         "primary/PrimaryMixer.cpp",
         "primary/StreamPrimary.cpp",
         "r_submix/ModuleRemoteSubmix.cpp",
         "r_submix/SubmixRoute.cpp",
         "r_submix/StreamRemoteSubmix.cpp",
+        "stub/DriverStubImpl.cpp",
         "stub/ModuleStub.cpp",
         "stub/StreamStub.cpp",
         "usb/ModuleUsb.cpp",
diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp
index 54e2d18..0ff8eb4 100644
--- a/audio/aidl/default/Configuration.cpp
+++ b/audio/aidl/default/Configuration.cpp
@@ -324,9 +324,9 @@
 //
 // Mix ports:
 //  * "r_submix output", maximum 10 opened streams, maximum 10 active streams
-//    - profile PCM 16-bit; STEREO; 8000, 11025, 16000, 32000, 44100, 48000
+//    - profile PCM 16-bit; STEREO; 8000, 11025, 16000, 32000, 44100, 48000, 192000
 //  * "r_submix input", maximum 10 opened streams, maximum 10 active streams
-//    - profile PCM 16-bit; STEREO; 8000, 11025, 16000, 32000, 44100, 48000
+//    - profile PCM 16-bit; STEREO; 8000, 11025, 16000, 32000, 44100, 48000, 192000
 //
 // Routes:
 //  "r_submix output" -> "Remote Submix Out"
@@ -337,7 +337,7 @@
         Configuration c;
         const std::vector<AudioProfile> remoteSubmixPcmAudioProfiles{
                 createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO},
-                              {8000, 11025, 16000, 32000, 44100, 48000})};
+                              {8000, 11025, 16000, 32000, 44100, 48000, 192000})};
 
         // Device ports
 
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index c14d06e..45ce5ef 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -47,6 +47,7 @@
 using aidl::android::media::audio::common::AudioDeviceType;
 using aidl::android::media::audio::common::AudioFormatDescription;
 using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::AudioGainConfig;
 using aidl::android::media::audio::common::AudioInputFlags;
 using aidl::android::media::audio::common::AudioIoFlags;
 using aidl::android::media::audio::common::AudioMMapPolicy;
@@ -1200,7 +1201,9 @@
     }
 
     if (in_requested.gain.has_value()) {
-        // Let's pretend that gain can always be applied.
+        if (!setAudioPortConfigGain(*portIt, in_requested.gain.value())) {
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
         out_suggested->gain = in_requested.gain.value();
     }
 
@@ -1242,6 +1245,52 @@
     return ndk::ScopedAStatus::ok();
 }
 
+bool Module::setAudioPortConfigGain(const AudioPort& port, const AudioGainConfig& gainRequested) {
+    auto& ports = getConfig().ports;
+    if (gainRequested.index < 0 || gainRequested.index >= (int)port.gains.size()) {
+        LOG(ERROR) << __func__ << ": gains for port " << port.id << " is undefined";
+        return false;
+    }
+    int stepValue = port.gains[gainRequested.index].stepValue;
+    if (stepValue == 0) {
+        LOG(ERROR) << __func__ << ": port gain step value is 0";
+        return false;
+    }
+    int minValue = port.gains[gainRequested.index].minValue;
+    int maxValue = port.gains[gainRequested.index].maxValue;
+    if (gainRequested.values[0] > maxValue || gainRequested.values[0] < minValue) {
+        LOG(ERROR) << __func__ << ": gain value " << gainRequested.values[0]
+                   << " out of range of min and max gain config";
+        return false;
+    }
+    int gainIndex = (gainRequested.values[0] - minValue) / stepValue;
+    int totalSteps = (maxValue - minValue) / stepValue;
+    if (totalSteps == 0) {
+        LOG(ERROR) << __func__ << ": difference between port gain min value " << minValue
+                   << " and max value " << maxValue << " is less than step value " << stepValue;
+        return false;
+    }
+    // Root-power quantities are used in curve:
+    // 10^((minMb / 100 + (maxMb / 100 - minMb / 100) * gainIndex / totalSteps) / (10 * 2))
+    // where 100 is the conversion from mB to dB, 10 comes from the log 10 conversion from power
+    // ratios, and 2 means are the square of amplitude.
+    float gain =
+            pow(10, (minValue + (maxValue - minValue) * (gainIndex / (float)totalSteps)) / 2000);
+    if (gain < 0) {
+        LOG(ERROR) << __func__ << ": gain " << gain << " is less than 0";
+        return false;
+    }
+    for (const auto& route : getConfig().routes) {
+        if (route.sinkPortId != port.id) {
+            continue;
+        }
+        for (const auto sourcePortId : route.sourcePortIds) {
+            mStreams.setGain(sourcePortId, gain);
+        }
+    }
+    return true;
+}
+
 ndk::ScopedAStatus Module::resetAudioPatch(int32_t in_patchId) {
     auto& patches = getConfig().patches;
     auto patchIt = findById<AudioPatch>(patches, in_patchId);
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 3e4650d..de66293 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -855,6 +855,11 @@
     return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus StreamCommonImpl::setGain(float gain) {
+    LOG(DEBUG) << __func__ << ": gain " << gain;
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
 ndk::ScopedAStatus StreamCommonImpl::bluetoothParametersUpdated() {
     LOG(DEBUG) << __func__;
     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
diff --git a/audio/aidl/default/alsa/StreamAlsa.cpp b/audio/aidl/default/alsa/StreamAlsa.cpp
index f548903..c77bfca 100644
--- a/audio/aidl/default/alsa/StreamAlsa.cpp
+++ b/audio/aidl/default/alsa/StreamAlsa.cpp
@@ -75,6 +75,10 @@
     }
     decltype(mAlsaDeviceProxies) alsaDeviceProxies;
     for (const auto& device : getDeviceProfiles()) {
+        if ((device.direction == PCM_OUT && mIsInput) ||
+            (device.direction == PCM_IN && !mIsInput)) {
+            continue;
+        }
         alsa::DeviceProxy proxy;
         if (device.isExternal) {
             // Always ask alsa configure as required since the configuration should be supported
@@ -92,6 +96,9 @@
         }
         alsaDeviceProxies.push_back(std::move(proxy));
     }
+    if (alsaDeviceProxies.empty()) {
+        return ::android::NO_INIT;
+    }
     mAlsaDeviceProxies = std::move(alsaDeviceProxies);
     return ::android::OK;
 }
@@ -110,6 +117,7 @@
                                 mReadWriteRetries);
         maxLatency = proxy_get_latency(mAlsaDeviceProxies[0].get());
     } else {
+        alsa::applyGain(buffer, mGain, bytesToTransfer, mConfig.value().format, mConfig->channels);
         for (auto& proxy : mAlsaDeviceProxies) {
             proxy_write_with_retries(proxy.get(), buffer, bytesToTransfer, mReadWriteRetries);
             maxLatency = std::max(maxLatency, proxy_get_latency(proxy.get()));
@@ -159,4 +167,9 @@
     mAlsaDeviceProxies.clear();
 }
 
+ndk::ScopedAStatus StreamAlsa::setGain(float gain) {
+    mGain = gain;
+    return ndk::ScopedAStatus::ok();
+}
+
 }  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/alsa/Utils.cpp b/audio/aidl/default/alsa/Utils.cpp
index 8eaf162..10374f2 100644
--- a/audio/aidl/default/alsa/Utils.cpp
+++ b/audio/aidl/default/alsa/Utils.cpp
@@ -22,6 +22,8 @@
 #include <aidl/android/media/audio/common/AudioFormatType.h>
 #include <aidl/android/media/audio/common/PcmType.h>
 #include <android-base/logging.h>
+#include <audio_utils/primitives.h>
+#include <cutils/compiler.h>
 
 #include "Utils.h"
 #include "core-impl/utils.h"
@@ -343,4 +345,68 @@
     return findValueOrDefault(getAudioFormatDescriptorToPcmFormatMap(), aidl, PCM_FORMAT_INVALID);
 }
 
+void applyGain(void* buffer, float gain, size_t bytesToTransfer, enum pcm_format pcmFormat,
+               int channelCount) {
+    if (channelCount != 1 && channelCount != 2) {
+        LOG(WARNING) << __func__ << ": unsupported channel count " << channelCount;
+        return;
+    }
+    if (!getPcmFormatToAudioFormatDescMap().contains(pcmFormat)) {
+        LOG(WARNING) << __func__ << ": unsupported pcm format " << pcmFormat;
+        return;
+    }
+    const float unityGainFloat = 1.0f;
+    if (std::abs(gain - unityGainFloat) < 1e-6) {
+        return;
+    }
+    int numFrames;
+    switch (pcmFormat) {
+        case PCM_FORMAT_S16_LE: {
+            const uint16_t unityGainQ4_12 = u4_12_from_float(unityGainFloat);
+            const uint16_t vl = u4_12_from_float(gain);
+            const uint32_t vrl = (vl << 16) | vl;
+            if (channelCount == 2) {
+                numFrames = bytesToTransfer / sizeof(uint32_t);
+                uint32_t* intBuffer = (uint32_t*)buffer;
+                if (CC_UNLIKELY(vl > unityGainQ4_12)) {
+                    // volume is boosted, so we might need to clamp even though
+                    // we process only one track.
+                    do {
+                        int32_t l = mulRL(1, *intBuffer, vrl) >> 12;
+                        int32_t r = mulRL(0, *intBuffer, vrl) >> 12;
+                        l = clamp16(l);
+                        r = clamp16(r);
+                        *intBuffer++ = (r << 16) | (l & 0xFFFF);
+                    } while (--numFrames);
+                } else {
+                    do {
+                        int32_t l = mulRL(1, *intBuffer, vrl) >> 12;
+                        int32_t r = mulRL(0, *intBuffer, vrl) >> 12;
+                        *intBuffer++ = (r << 16) | (l & 0xFFFF);
+                    } while (--numFrames);
+                }
+            } else {
+                numFrames = bytesToTransfer / sizeof(uint16_t);
+                int16_t* intBuffer = (int16_t*)buffer;
+                if (CC_UNLIKELY(vl > unityGainQ4_12)) {
+                    // volume is boosted, so we might need to clamp even though
+                    // we process only one track.
+                    do {
+                        int32_t mono = mulRL(1, *intBuffer, vrl) >> 12;
+                        *intBuffer++ = clamp16(mono);
+                    } while (--numFrames);
+                } else {
+                    do {
+                        int32_t mono = mulRL(1, *intBuffer, vrl) >> 12;
+                        *intBuffer++ = static_cast<int16_t>(mono & 0xFFFF);
+                    } while (--numFrames);
+                }
+            }
+        } break;
+        default:
+            // TODO(336370745): Implement gain for other supported formats
+            break;
+    }
+}
+
 }  // namespace aidl::android::hardware::audio::core::alsa
diff --git a/audio/aidl/default/alsa/Utils.h b/audio/aidl/default/alsa/Utils.h
index 980f685..a97ea10 100644
--- a/audio/aidl/default/alsa/Utils.h
+++ b/audio/aidl/default/alsa/Utils.h
@@ -59,6 +59,8 @@
     AlsaProxy mProxy;
 };
 
+void applyGain(void* buffer, float gain, size_t bytesToTransfer, enum pcm_format pcmFormat,
+               int channelCount);
 ::aidl::android::media::audio::common::AudioChannelLayout getChannelLayoutMaskFromChannelCount(
         unsigned int channelCount, int isInput);
 ::aidl::android::media::audio::common::AudioChannelLayout getChannelIndexMaskFromChannelCount(
diff --git a/audio/aidl/default/config/audioPolicy/api/current.txt b/audio/aidl/default/config/audioPolicy/api/current.txt
index 3547f54..e57c108 100644
--- a/audio/aidl/default/config/audioPolicy/api/current.txt
+++ b/audio/aidl/default/config/audioPolicy/api/current.txt
@@ -191,6 +191,7 @@
     enum_constant public static final android.audio.policy.configuration.AudioFormat AUDIO_FORMAT_AAC_XHE;
     enum_constant public static final android.audio.policy.configuration.AudioFormat AUDIO_FORMAT_AC3;
     enum_constant public static final android.audio.policy.configuration.AudioFormat AUDIO_FORMAT_AC4;
+    enum_constant public static final android.audio.policy.configuration.AudioFormat AUDIO_FORMAT_AC4_L4;
     enum_constant public static final android.audio.policy.configuration.AudioFormat AUDIO_FORMAT_ALAC;
     enum_constant public static final android.audio.policy.configuration.AudioFormat AUDIO_FORMAT_AMR_NB;
     enum_constant public static final android.audio.policy.configuration.AudioFormat AUDIO_FORMAT_AMR_WB;
diff --git a/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd b/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd
index d93f697..108a6a3 100644
--- a/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd
+++ b/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd
@@ -389,6 +389,7 @@
             <xs:enumeration value="AUDIO_FORMAT_APTX"/>
             <xs:enumeration value="AUDIO_FORMAT_APTX_HD"/>
             <xs:enumeration value="AUDIO_FORMAT_AC4"/>
+            <xs:enumeration value="AUDIO_FORMAT_AC4_L4"/>
             <xs:enumeration value="AUDIO_FORMAT_LDAC"/>
             <xs:enumeration value="AUDIO_FORMAT_MAT"/>
             <xs:enumeration value="AUDIO_FORMAT_MAT_1_0"/>
diff --git a/audio/aidl/default/StreamSwitcher.cpp b/audio/aidl/default/deprecated/StreamSwitcher.cpp
similarity index 95%
rename from audio/aidl/default/StreamSwitcher.cpp
rename to audio/aidl/default/deprecated/StreamSwitcher.cpp
index 8ba15a8..f1e0f52 100644
--- a/audio/aidl/default/StreamSwitcher.cpp
+++ b/audio/aidl/default/deprecated/StreamSwitcher.cpp
@@ -23,12 +23,12 @@
 #include <error/expected_utils.h>
 
 #include "core-impl/StreamStub.h"
-#include "core-impl/StreamSwitcher.h"
+#include "deprecated/StreamSwitcher.h"
 
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::media::audio::common::AudioDevice;
 
-namespace aidl::android::hardware::audio::core {
+namespace aidl::android::hardware::audio::core::deprecated {
 
 StreamSwitcher::StreamSwitcher(StreamContext* context, const Metadata& metadata)
     : mContext(context),
@@ -260,4 +260,12 @@
     return mStream->bluetoothParametersUpdated();
 }
 
-}  // namespace aidl::android::hardware::audio::core
+ndk::ScopedAStatus StreamSwitcher::setGain(float gain) {
+    if (mStream == nullptr) {
+        LOG(ERROR) << __func__ << ": stream was closed";
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    return mStream->setGain(gain);
+}
+
+}  // namespace aidl::android::hardware::audio::core::deprecated
diff --git a/audio/aidl/default/include/core-impl/StreamSwitcher.h b/audio/aidl/default/deprecated/StreamSwitcher.h
similarity index 95%
rename from audio/aidl/default/include/core-impl/StreamSwitcher.h
rename to audio/aidl/default/deprecated/StreamSwitcher.h
index 5764ad6..56fdd23 100644
--- a/audio/aidl/default/include/core-impl/StreamSwitcher.h
+++ b/audio/aidl/default/deprecated/StreamSwitcher.h
@@ -14,11 +14,18 @@
  * limitations under the License.
  */
 
+/**
+ ** This class is deprecated because its use causes threading issues
+ ** with the FMQ due to change of threads reading and writing into FMQ.
+ **
+ ** DO NOT USE. These files will be removed.
+ **/
+
 #pragma once
 
-#include "Stream.h"
+#include "core-impl/Stream.h"
 
-namespace aidl::android::hardware::audio::core {
+namespace aidl::android::hardware::audio::core::deprecated {
 
 // 'StreamSwitcher' is an implementation of 'StreamCommonInterface' which allows
 // dynamically switching the underlying stream implementation based on currently
@@ -130,6 +137,7 @@
             const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
             override;
     ndk::ScopedAStatus bluetoothParametersUpdated() override;
+    ndk::ScopedAStatus setGain(float gain) override;
 
   protected:
     // Since switching a stream requires closing down the current stream, StreamSwitcher
@@ -191,4 +199,4 @@
     bool mBluetoothParametersUpdated = false;
 };
 
-}  // namespace aidl::android::hardware::audio::core
+}  // namespace aidl::android::hardware::audio::core::deprecated
diff --git a/audio/aidl/default/include/core-impl/DriverStubImpl.h b/audio/aidl/default/include/core-impl/DriverStubImpl.h
new file mode 100644
index 0000000..40a9fea
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/DriverStubImpl.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "core-impl/Stream.h"
+
+namespace aidl::android::hardware::audio::core {
+
+class DriverStubImpl : virtual public DriverInterface {
+  public:
+    explicit DriverStubImpl(const StreamContext& context);
+
+    ::android::status_t init() override;
+    ::android::status_t drain(StreamDescriptor::DrainMode) override;
+    ::android::status_t flush() override;
+    ::android::status_t pause() override;
+    ::android::status_t standby() override;
+    ::android::status_t start() override;
+    ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
+                                 int32_t* latencyMs) override;
+    void shutdown() override;
+
+  private:
+    const size_t mBufferSizeFrames;
+    const size_t mFrameSizeBytes;
+    const int mSampleRate;
+    const bool mIsAsynchronous;
+    const bool mIsInput;
+    bool mIsInitialized = false;  // Used for validating the state machine logic.
+    bool mIsStandby = true;       // Used for validating the state machine logic.
+    int64_t mStartTimeNs = 0;
+    long mFramesSinceStart = 0;
+};
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 00eeb4e..8548aff 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -263,6 +263,9 @@
             ::aidl::android::media::audio::common::AudioPortConfig* out_suggested, bool* applied);
     ndk::ScopedAStatus updateStreamsConnectedState(const AudioPatch& oldPatch,
                                                    const AudioPatch& newPatch);
+    bool setAudioPortConfigGain(
+            const ::aidl::android::media::audio::common::AudioPort& port,
+            const ::aidl::android::media::audio::common::AudioGainConfig& gainRequested);
 };
 
 std::ostream& operator<<(std::ostream& os, Module::Type t);
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 100b4c8..f7b9269 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -38,6 +38,7 @@
 #include <aidl/android/media/audio/common/AudioIoFlags.h>
 #include <aidl/android/media/audio/common/AudioOffloadInfo.h>
 #include <aidl/android/media/audio/common/MicrophoneInfo.h>
+#include <android-base/thread_annotations.h>
 #include <error/expected_utils.h>
 #include <fmq/AidlMessageQueue.h>
 #include <system/thread_defs.h>
@@ -132,6 +133,9 @@
     ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
     int getTransientStateDelayMs() const { return mDebugParameters.transientStateDelayMs; }
     int getSampleRate() const { return mSampleRate; }
+    bool isInput() const {
+        return mFlags.getTag() == ::aidl::android::media::audio::common::AudioIoFlags::input;
+    }
     bool isValid() const;
     // 'reset' is called on a Binder thread when closing the stream. Does not use
     // locking because it only cleans MQ pointers which were also set on the Binder thread.
@@ -342,6 +346,7 @@
     virtual ndk::ScopedAStatus setConnectedDevices(
             const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) = 0;
     virtual ndk::ScopedAStatus bluetoothParametersUpdated() = 0;
+    virtual ndk::ScopedAStatus setGain(float gain) = 0;
 };
 
 // This is equivalent to automatically generated 'IStreamCommonDelegator' but uses
@@ -443,6 +448,7 @@
             const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
             override;
     ndk::ScopedAStatus bluetoothParametersUpdated() override;
+    ndk::ScopedAStatus setGain(float gain) override;
 
   protected:
     static StreamWorkerInterface::CreateInstance getDefaultInWorkerCreator() {
@@ -609,6 +615,12 @@
         return ndk::ScopedAStatus::ok();
     }
 
+    ndk::ScopedAStatus setGain(float gain) {
+        auto s = mStream.lock();
+        if (s) return s->setGain(gain);
+        return ndk::ScopedAStatus::ok();
+    }
+
   private:
     std::weak_ptr<StreamCommonInterface> mStream;
     ndk::SpAIBinder mStreamBinder;
@@ -644,6 +656,12 @@
         return isOk ? ndk::ScopedAStatus::ok()
                     : ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
     }
+    ndk::ScopedAStatus setGain(int32_t portId, float gain) {
+        if (auto it = mStreams.find(portId); it != mStreams.end()) {
+            return it->second.setGain(gain);
+        }
+        return ndk::ScopedAStatus::ok();
+    }
 
   private:
     // Maps port ids and port config ids to streams. Multimap because a port
diff --git a/audio/aidl/default/include/core-impl/StreamAlsa.h b/audio/aidl/default/include/core-impl/StreamAlsa.h
index 0356946..8bdf208 100644
--- a/audio/aidl/default/include/core-impl/StreamAlsa.h
+++ b/audio/aidl/default/include/core-impl/StreamAlsa.h
@@ -45,6 +45,7 @@
                                  int32_t* latencyMs) override;
     ::android::status_t refinePosition(StreamDescriptor::Position* position) override;
     void shutdown() override;
+    ndk::ScopedAStatus setGain(float gain) override;
 
   protected:
     // Called from 'start' to initialize 'mAlsaDeviceProxies', the vector must be non-empty.
@@ -58,6 +59,9 @@
     const int mReadWriteRetries;
     // All fields below are only used on the worker thread.
     std::vector<alsa::DeviceProxy> mAlsaDeviceProxies;
+
+  private:
+    std::atomic<float> mGain = 1.0;
 };
 
 }  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/StreamPrimary.h b/audio/aidl/default/include/core-impl/StreamPrimary.h
index 8d5c57d..4f19a46 100644
--- a/audio/aidl/default/include/core-impl/StreamPrimary.h
+++ b/audio/aidl/default/include/core-impl/StreamPrimary.h
@@ -16,10 +16,14 @@
 
 #pragma once
 
+#include <mutex>
 #include <vector>
 
+#include <android-base/thread_annotations.h>
+
+#include "DriverStubImpl.h"
 #include "StreamAlsa.h"
-#include "StreamSwitcher.h"
+#include "primary/PrimaryMixer.h"
 
 namespace aidl::android::hardware::audio::core {
 
@@ -27,21 +31,54 @@
   public:
     StreamPrimary(StreamContext* context, const Metadata& metadata);
 
+    // Methods of 'DriverInterface'.
+    ::android::status_t init() override;
+    ::android::status_t drain(StreamDescriptor::DrainMode mode) override;
+    ::android::status_t flush() override;
+    ::android::status_t pause() override;
+    ::android::status_t standby() override;
     ::android::status_t start() override;
     ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                  int32_t* latencyMs) override;
     ::android::status_t refinePosition(StreamDescriptor::Position* position) override;
+    void shutdown() override;
+
+    // Overridden methods of 'StreamCommonImpl', called on a Binder thread.
+    ndk::ScopedAStatus setConnectedDevices(const ConnectedDevices& devices) override;
 
   protected:
     std::vector<alsa::DeviceProfile> getDeviceProfiles() override;
+    bool isStubStream();
 
     const bool mIsAsynchronous;
     int64_t mStartTimeNs = 0;
     long mFramesSinceStart = 0;
     bool mSkipNextTransfer = false;
+
+  private:
+    using AlsaDeviceId = std::pair<int, int>;
+
+    static constexpr StreamPrimary::AlsaDeviceId kDefaultCardAndDeviceId{
+            primary::PrimaryMixer::kAlsaCard, primary::PrimaryMixer::kAlsaDevice};
+    static constexpr StreamPrimary::AlsaDeviceId kStubDeviceId{
+            primary::PrimaryMixer::kInvalidAlsaCard, primary::PrimaryMixer::kInvalidAlsaDevice};
+
+    static AlsaDeviceId getCardAndDeviceId(
+            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices);
+    static bool useStubStream(bool isInput,
+                              const ::aidl::android::media::audio::common::AudioDevice& device);
+
+    bool isStubStreamOnWorker() const { return mCurrAlsaDeviceId == kStubDeviceId; }
+
+    DriverStubImpl mStubDriver;
+    mutable std::mutex mLock;
+    AlsaDeviceId mAlsaDeviceId GUARDED_BY(mLock) = kStubDeviceId;
+
+    // Used by the worker thread only.
+    AlsaDeviceId mCurrAlsaDeviceId = kStubDeviceId;
 };
 
-class StreamInPrimary final : public StreamIn, public StreamSwitcher, public StreamInHwGainHelper {
+class StreamInPrimary final : public StreamIn, public StreamPrimary, public StreamInHwGainHelper {
   public:
     friend class ndk::SharedRefBase;
     StreamInPrimary(
@@ -50,14 +87,6 @@
             const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
 
   private:
-    static bool useStubStream(const ::aidl::android::media::audio::common::AudioDevice& device);
-
-    DeviceSwitchBehavior switchCurrentStream(
-            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
-            override;
-    std::unique_ptr<StreamCommonInterfaceEx> createNewStream(
-            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
-            StreamContext* context, const Metadata& metadata) override;
     void onClose(StreamDescriptor::State) override { defaultOnClose(); }
 
     ndk::ScopedAStatus getHwGain(std::vector<float>* _aidl_return) override;
@@ -65,7 +94,7 @@
 };
 
 class StreamOutPrimary final : public StreamOut,
-                               public StreamSwitcher,
+                               public StreamPrimary,
                                public StreamOutHwVolumeHelper {
   public:
     friend class ndk::SharedRefBase;
@@ -75,22 +104,10 @@
                              offloadInfo);
 
   private:
-    static bool useStubStream(const ::aidl::android::media::audio::common::AudioDevice& device);
-
-    DeviceSwitchBehavior switchCurrentStream(
-            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
-            override;
-    std::unique_ptr<StreamCommonInterfaceEx> createNewStream(
-            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
-            StreamContext* context, const Metadata& metadata) override;
     void onClose(StreamDescriptor::State) override { defaultOnClose(); }
 
     ndk::ScopedAStatus getHwVolume(std::vector<float>* _aidl_return) override;
     ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;
-
-    ndk::ScopedAStatus setConnectedDevices(
-            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
-            override;
 };
 
 }  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
index 6ea7968..e78e8b7 100644
--- a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
+++ b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
@@ -16,19 +16,18 @@
 
 #pragma once
 
+#include <atomic>
+#include <mutex>
 #include <vector>
 
 #include "core-impl/Stream.h"
-#include "core-impl/StreamSwitcher.h"
 #include "r_submix/SubmixRoute.h"
 
 namespace aidl::android::hardware::audio::core {
 
 class StreamRemoteSubmix : public StreamCommonImpl {
   public:
-    StreamRemoteSubmix(
-            StreamContext* context, const Metadata& metadata,
-            const ::aidl::android::media::audio::common::AudioDeviceAddress& deviceAddress);
+    StreamRemoteSubmix(StreamContext* context, const Metadata& metadata);
     ~StreamRemoteSubmix();
 
     // Methods of 'DriverInterface'.
@@ -45,17 +44,18 @@
 
     // Overridden methods of 'StreamCommonImpl', called on a Binder thread.
     ndk::ScopedAStatus prepareToClose() override;
+    ndk::ScopedAStatus setConnectedDevices(const ConnectedDevices& devices) override;
 
   private:
     long getDelayInUsForFrameCount(size_t frameCount);
+    ::aidl::android::media::audio::common::AudioDeviceAddress getDeviceAddress() const {
+        std::lock_guard guard(mLock);
+        return mDeviceAddress;
+    }
     size_t getStreamPipeSizeInFrames();
-    ::android::status_t outWrite(void* buffer, size_t frameCount, size_t* actualFrameCount);
     ::android::status_t inRead(void* buffer, size_t frameCount, size_t* actualFrameCount);
-
-    const ::aidl::android::media::audio::common::AudioDeviceAddress mDeviceAddress;
-    const bool mIsInput;
-    r_submix::AudioConfig mStreamConfig;
-    std::shared_ptr<r_submix::SubmixRoute> mCurrentRoute = nullptr;
+    ::android::status_t outWrite(void* buffer, size_t frameCount, size_t* actualFrameCount);
+    ::android::status_t setCurrentRoute();
 
     // Limit for the number of error log entries to avoid spamming the logs.
     static constexpr int kMaxErrorLogs = 5;
@@ -66,6 +66,15 @@
     // 5ms between two read attempts when pipe is empty
     static constexpr int kReadAttemptSleepUs = 5000;
 
+    const bool mIsInput;
+    const r_submix::AudioConfig mStreamConfig;
+
+    mutable std::mutex mLock;
+    ::aidl::android::media::audio::common::AudioDeviceAddress mDeviceAddress GUARDED_BY(mLock);
+    std::atomic<bool> mDeviceAddressUpdated = false;
+
+    // Used by the worker thread only.
+    std::shared_ptr<r_submix::SubmixRoute> mCurrentRoute = nullptr;
     int64_t mStartTimeNs = 0;
     long mFramesSinceStart = 0;
     int mReadErrorCount = 0;
@@ -73,7 +82,7 @@
     int mWriteShutdownCount = 0;
 };
 
-class StreamInRemoteSubmix final : public StreamIn, public StreamSwitcher {
+class StreamInRemoteSubmix final : public StreamIn, public StreamRemoteSubmix {
   public:
     friend class ndk::SharedRefBase;
     StreamInRemoteSubmix(
@@ -82,19 +91,13 @@
             const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
 
   private:
-    DeviceSwitchBehavior switchCurrentStream(
-            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
-            override;
-    std::unique_ptr<StreamCommonInterfaceEx> createNewStream(
-            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
-            StreamContext* context, const Metadata& metadata) override;
     void onClose(StreamDescriptor::State) override { defaultOnClose(); }
     ndk::ScopedAStatus getActiveMicrophones(
             std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
             override;
 };
 
-class StreamOutRemoteSubmix final : public StreamOut, public StreamSwitcher {
+class StreamOutRemoteSubmix final : public StreamOut, public StreamRemoteSubmix {
   public:
     friend class ndk::SharedRefBase;
     StreamOutRemoteSubmix(
@@ -104,12 +107,6 @@
                     offloadInfo);
 
   private:
-    DeviceSwitchBehavior switchCurrentStream(
-            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
-            override;
-    std::unique_ptr<StreamCommonInterfaceEx> createNewStream(
-            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
-            StreamContext* context, const Metadata& metadata) override;
     void onClose(StreamDescriptor::State) override { defaultOnClose(); }
 };
 
diff --git a/audio/aidl/default/include/core-impl/StreamStub.h b/audio/aidl/default/include/core-impl/StreamStub.h
index 22b2020..cee44db 100644
--- a/audio/aidl/default/include/core-impl/StreamStub.h
+++ b/audio/aidl/default/include/core-impl/StreamStub.h
@@ -16,38 +16,15 @@
 
 #pragma once
 
+#include "core-impl/DriverStubImpl.h"
 #include "core-impl/Stream.h"
 
 namespace aidl::android::hardware::audio::core {
 
-class StreamStub : public StreamCommonImpl {
+class StreamStub : public StreamCommonImpl, public DriverStubImpl {
   public:
     StreamStub(StreamContext* context, const Metadata& metadata);
     ~StreamStub();
-
-    // Methods of 'DriverInterface'.
-    ::android::status_t init() override;
-    ::android::status_t drain(StreamDescriptor::DrainMode) override;
-    ::android::status_t flush() override;
-    ::android::status_t pause() override;
-    ::android::status_t standby() override;
-    ::android::status_t start() override;
-    ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
-                                 int32_t* latencyMs) override;
-    void shutdown() override;
-
-  private:
-    const size_t mBufferSizeFrames;
-    const size_t mFrameSizeBytes;
-    const int mSampleRate;
-    const bool mIsAsynchronous;
-    const bool mIsInput;
-    bool mIsInitialized = false;  // Used for validating the state machine logic.
-    bool mIsStandby = true;       // Used for validating the state machine logic.
-
-    // Used by the worker thread.
-    int64_t mStartTimeNs = 0;
-    long mFramesSinceStart = 0;
 };
 
 class StreamInStub final : public StreamIn, public StreamStub {
diff --git a/audio/aidl/default/primary/PrimaryMixer.h b/audio/aidl/default/primary/PrimaryMixer.h
index 3806428..760d42f 100644
--- a/audio/aidl/default/primary/PrimaryMixer.h
+++ b/audio/aidl/default/primary/PrimaryMixer.h
@@ -16,20 +16,14 @@
 
 #pragma once
 
-#include <map>
-#include <memory>
-#include <mutex>
-#include <vector>
-
-#include <android-base/thread_annotations.h>
-#include <android/binder_auto_utils.h>
-
 #include "alsa/Mixer.h"
 
 namespace aidl::android::hardware::audio::core::primary {
 
 class PrimaryMixer : public alsa::Mixer {
   public:
+    static constexpr int kInvalidAlsaCard = -1;
+    static constexpr int kInvalidAlsaDevice = -1;
     static constexpr int kAlsaCard = 0;
     static constexpr int kAlsaDevice = 0;
 
diff --git a/audio/aidl/default/primary/StreamPrimary.cpp b/audio/aidl/default/primary/StreamPrimary.cpp
index 7325a91..c1c1f03 100644
--- a/audio/aidl/default/primary/StreamPrimary.cpp
+++ b/audio/aidl/default/primary/StreamPrimary.cpp
@@ -15,19 +15,22 @@
  */
 
 #define LOG_TAG "AHAL_StreamPrimary"
+
+#include <cstdio>
+
 #include <android-base/logging.h>
+#include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <audio_utils/clock.h>
 #include <error/Result.h>
 #include <error/expected_utils.h>
 
-#include "PrimaryMixer.h"
 #include "core-impl/StreamPrimary.h"
-#include "core-impl/StreamStub.h"
 
 using aidl::android::hardware::audio::common::SinkMetadata;
 using aidl::android::hardware::audio::common::SourceMetadata;
 using aidl::android::media::audio::common::AudioDevice;
+using aidl::android::media::audio::common::AudioDeviceAddress;
 using aidl::android::media::audio::common::AudioDeviceDescription;
 using aidl::android::media::audio::common::AudioDeviceType;
 using aidl::android::media::audio::common::AudioOffloadInfo;
@@ -38,11 +41,49 @@
 
 StreamPrimary::StreamPrimary(StreamContext* context, const Metadata& metadata)
     : StreamAlsa(context, metadata, 3 /*readWriteRetries*/),
-      mIsAsynchronous(!!getContext().getAsyncCallback()) {
+      mIsAsynchronous(!!getContext().getAsyncCallback()),
+      mStubDriver(getContext()) {
     context->startStreamDataProcessor();
 }
 
+::android::status_t StreamPrimary::init() {
+    RETURN_STATUS_IF_ERROR(mStubDriver.init());
+    return StreamAlsa::init();
+}
+
+::android::status_t StreamPrimary::drain(StreamDescriptor::DrainMode mode) {
+    return isStubStreamOnWorker() ? mStubDriver.drain(mode) : StreamAlsa::drain(mode);
+}
+
+::android::status_t StreamPrimary::flush() {
+    RETURN_STATUS_IF_ERROR(isStubStreamOnWorker() ? mStubDriver.flush() : StreamAlsa::flush());
+    // TODO(b/372951987): consider if this needs to be done from 'StreamInWorkerLogic::cycle'.
+    return mIsInput ? standby() : ::android::OK;
+}
+
+::android::status_t StreamPrimary::pause() {
+    return isStubStreamOnWorker() ? mStubDriver.pause() : StreamAlsa::pause();
+}
+
+::android::status_t StreamPrimary::standby() {
+    return isStubStreamOnWorker() ? mStubDriver.standby() : StreamAlsa::standby();
+}
+
 ::android::status_t StreamPrimary::start() {
+    bool isStub = true, shutdownAlsaStream = false;
+    {
+        std::lock_guard l(mLock);
+        isStub = mAlsaDeviceId == kStubDeviceId;
+        shutdownAlsaStream =
+                mCurrAlsaDeviceId != mAlsaDeviceId && mCurrAlsaDeviceId != kStubDeviceId;
+        mCurrAlsaDeviceId = mAlsaDeviceId;
+    }
+    if (shutdownAlsaStream) {
+        StreamAlsa::shutdown();  // Close currently opened ALSA devices.
+    }
+    if (isStub) {
+        return mStubDriver.start();
+    }
     RETURN_STATUS_IF_ERROR(StreamAlsa::start());
     mStartTimeNs = ::android::uptimeNanos();
     mFramesSinceStart = 0;
@@ -52,6 +93,9 @@
 
 ::android::status_t StreamPrimary::transfer(void* buffer, size_t frameCount,
                                             size_t* actualFrameCount, int32_t* latencyMs) {
+    if (isStubStreamOnWorker()) {
+        return mStubDriver.transfer(buffer, frameCount, actualFrameCount, latencyMs);
+    }
     // This is a workaround for the emulator implementation which has a host-side buffer
     // and is not being able to achieve real-time behavior similar to ADSPs (b/302587331).
     if (!mSkipNextTransfer) {
@@ -91,63 +135,85 @@
     return ::android::OK;
 }
 
+void StreamPrimary::shutdown() {
+    StreamAlsa::shutdown();
+    mStubDriver.shutdown();
+}
+
+ndk::ScopedAStatus StreamPrimary::setConnectedDevices(const ConnectedDevices& devices) {
+    LOG(DEBUG) << __func__ << ": " << ::android::internal::ToString(devices);
+    if (devices.size() > 1) {
+        LOG(ERROR) << __func__ << ": primary stream can only be connected to one device, got: "
+                   << devices.size();
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+    {
+        const bool useStubDriver = devices.empty() || useStubStream(mIsInput, devices[0]);
+        std::lock_guard l(mLock);
+        mAlsaDeviceId = useStubDriver ? kStubDeviceId : getCardAndDeviceId(devices);
+    }
+    if (!devices.empty()) {
+        auto streamDataProcessor = getContext().getStreamDataProcessor().lock();
+        if (streamDataProcessor != nullptr) {
+            streamDataProcessor->setAudioDevice(devices[0]);
+        }
+    }
+    return StreamAlsa::setConnectedDevices(devices);
+}
+
 std::vector<alsa::DeviceProfile> StreamPrimary::getDeviceProfiles() {
-    static const std::vector<alsa::DeviceProfile> kBuiltInSource{
-            alsa::DeviceProfile{.card = primary::PrimaryMixer::kAlsaCard,
-                                .device = primary::PrimaryMixer::kAlsaDevice,
-                                .direction = PCM_IN,
+    return {alsa::DeviceProfile{.card = mCurrAlsaDeviceId.first,
+                                .device = mCurrAlsaDeviceId.second,
+                                .direction = mIsInput ? PCM_IN : PCM_OUT,
                                 .isExternal = false}};
-    static const std::vector<alsa::DeviceProfile> kBuiltInSink{
-            alsa::DeviceProfile{.card = primary::PrimaryMixer::kAlsaCard,
-                                .device = primary::PrimaryMixer::kAlsaDevice,
-                                .direction = PCM_OUT,
-                                .isExternal = false}};
-    return mIsInput ? kBuiltInSource : kBuiltInSink;
+}
+
+bool StreamPrimary::isStubStream() {
+    std::lock_guard l(mLock);
+    return mAlsaDeviceId == kStubDeviceId;
+}
+
+// static
+StreamPrimary::AlsaDeviceId StreamPrimary::getCardAndDeviceId(
+        const std::vector<AudioDevice>& devices) {
+    if (devices.empty() || devices[0].address.getTag() != AudioDeviceAddress::id) {
+        return kDefaultCardAndDeviceId;
+    }
+    std::string deviceAddress = devices[0].address.get<AudioDeviceAddress::id>();
+    AlsaDeviceId cardAndDeviceId;
+    if (const size_t suffixPos = deviceAddress.rfind("CARD_");
+        suffixPos == std::string::npos ||
+        sscanf(deviceAddress.c_str() + suffixPos, "CARD_%d_DEV_%d", &cardAndDeviceId.first,
+               &cardAndDeviceId.second) != 2) {
+        return kDefaultCardAndDeviceId;
+    }
+    LOG(DEBUG) << __func__ << ": parsed with card id " << cardAndDeviceId.first << ", device id "
+               << cardAndDeviceId.second;
+    return cardAndDeviceId;
+}
+
+// static
+bool StreamPrimary::useStubStream(
+        bool isInput, const ::aidl::android::media::audio::common::AudioDevice& device) {
+    static const bool kSimulateInput =
+            GetBoolProperty("ro.boot.audio.tinyalsa.simulate_input", false);
+    static const bool kSimulateOutput =
+            GetBoolProperty("ro.boot.audio.tinyalsa.ignore_output", false);
+    if (isInput) {
+        return kSimulateInput || device.type.type == AudioDeviceType::IN_TELEPHONY_RX ||
+               device.type.type == AudioDeviceType::IN_FM_TUNER ||
+               device.type.connection == AudioDeviceDescription::CONNECTION_BUS /*deprecated */;
+    }
+    return kSimulateOutput || device.type.type == AudioDeviceType::OUT_TELEPHONY_TX ||
+           device.type.connection == AudioDeviceDescription::CONNECTION_BUS /*deprecated*/;
 }
 
 StreamInPrimary::StreamInPrimary(StreamContext&& context, const SinkMetadata& sinkMetadata,
                                  const std::vector<MicrophoneInfo>& microphones)
     : StreamIn(std::move(context), microphones),
-      StreamSwitcher(&mContextInstance, sinkMetadata),
+      StreamPrimary(&mContextInstance, sinkMetadata),
       StreamInHwGainHelper(&mContextInstance) {}
 
-bool StreamInPrimary::useStubStream(const AudioDevice& device) {
-    static const bool kSimulateInput =
-            GetBoolProperty("ro.boot.audio.tinyalsa.simulate_input", false);
-    return kSimulateInput || device.type.type == AudioDeviceType::IN_TELEPHONY_RX ||
-           device.type.type == AudioDeviceType::IN_FM_TUNER ||
-           device.type.connection == AudioDeviceDescription::CONNECTION_BUS /*deprecated */ ||
-           (device.type.type == AudioDeviceType::IN_BUS && device.type.connection.empty());
-}
-
-StreamSwitcher::DeviceSwitchBehavior StreamInPrimary::switchCurrentStream(
-        const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
-    LOG(DEBUG) << __func__;
-    if (devices.size() > 1) {
-        LOG(ERROR) << __func__ << ": primary stream can only be connected to one device, got: "
-                   << devices.size();
-        return DeviceSwitchBehavior::UNSUPPORTED_DEVICES;
-    }
-    if (devices.empty() || useStubStream(devices[0]) == isStubStream()) {
-        return DeviceSwitchBehavior::USE_CURRENT_STREAM;
-    }
-    return DeviceSwitchBehavior::CREATE_NEW_STREAM;
-}
-
-std::unique_ptr<StreamCommonInterfaceEx> StreamInPrimary::createNewStream(
-        const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
-        StreamContext* context, const Metadata& metadata) {
-    if (devices.empty()) {
-        LOG(FATAL) << __func__ << ": called with empty devices";  // see 'switchCurrentStream'
-    }
-    if (useStubStream(devices[0])) {
-        return std::unique_ptr<StreamCommonInterfaceEx>(
-                new InnerStreamWrapper<StreamStub>(context, metadata));
-    }
-    return std::unique_ptr<StreamCommonInterfaceEx>(
-            new InnerStreamWrapper<StreamPrimary>(context, metadata));
-}
-
 ndk::ScopedAStatus StreamInPrimary::getHwGain(std::vector<float>* _aidl_return) {
     if (isStubStream()) {
         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
@@ -181,45 +247,9 @@
 StreamOutPrimary::StreamOutPrimary(StreamContext&& context, const SourceMetadata& sourceMetadata,
                                    const std::optional<AudioOffloadInfo>& offloadInfo)
     : StreamOut(std::move(context), offloadInfo),
-      StreamSwitcher(&mContextInstance, sourceMetadata),
+      StreamPrimary(&mContextInstance, sourceMetadata),
       StreamOutHwVolumeHelper(&mContextInstance) {}
 
-bool StreamOutPrimary::useStubStream(const AudioDevice& device) {
-    static const bool kSimulateOutput =
-            GetBoolProperty("ro.boot.audio.tinyalsa.ignore_output", false);
-    return kSimulateOutput || device.type.type == AudioDeviceType::OUT_TELEPHONY_TX ||
-           device.type.connection == AudioDeviceDescription::CONNECTION_BUS /*deprecated*/ ||
-           (device.type.type == AudioDeviceType::OUT_BUS && device.type.connection.empty());
-}
-
-StreamSwitcher::DeviceSwitchBehavior StreamOutPrimary::switchCurrentStream(
-        const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
-    LOG(DEBUG) << __func__;
-    if (devices.size() > 1) {
-        LOG(ERROR) << __func__ << ": primary stream can only be connected to one device, got: "
-                   << devices.size();
-        return DeviceSwitchBehavior::UNSUPPORTED_DEVICES;
-    }
-    if (devices.empty() || useStubStream(devices[0]) == isStubStream()) {
-        return DeviceSwitchBehavior::USE_CURRENT_STREAM;
-    }
-    return DeviceSwitchBehavior::CREATE_NEW_STREAM;
-}
-
-std::unique_ptr<StreamCommonInterfaceEx> StreamOutPrimary::createNewStream(
-        const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
-        StreamContext* context, const Metadata& metadata) {
-    if (devices.empty()) {
-        LOG(FATAL) << __func__ << ": called with empty devices";  // see 'switchCurrentStream'
-    }
-    if (useStubStream(devices[0])) {
-        return std::unique_ptr<StreamCommonInterfaceEx>(
-                new InnerStreamWrapper<StreamStub>(context, metadata));
-    }
-    return std::unique_ptr<StreamCommonInterfaceEx>(
-            new InnerStreamWrapper<StreamPrimary>(context, metadata));
-}
-
 ndk::ScopedAStatus StreamOutPrimary::getHwVolume(std::vector<float>* _aidl_return) {
     if (isStubStream()) {
         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
@@ -245,15 +275,4 @@
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus StreamOutPrimary::setConnectedDevices(
-        const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
-    if (!devices.empty()) {
-        auto streamDataProcessor = mContextInstance.getStreamDataProcessor().lock();
-        if (streamDataProcessor != nullptr) {
-            streamDataProcessor->setAudioDevice(devices[0]);
-        }
-    }
-    return StreamSwitcher::setConnectedDevices(devices);
-}
-
 }  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index db105b6..93fe028 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -26,128 +26,106 @@
 using aidl::android::hardware::audio::common::SourceMetadata;
 using aidl::android::hardware::audio::core::r_submix::SubmixRoute;
 using aidl::android::media::audio::common::AudioDeviceAddress;
+using aidl::android::media::audio::common::AudioDeviceType;
 using aidl::android::media::audio::common::AudioOffloadInfo;
 using aidl::android::media::audio::common::MicrophoneDynamicInfo;
 using aidl::android::media::audio::common::MicrophoneInfo;
 
 namespace aidl::android::hardware::audio::core {
 
-StreamRemoteSubmix::StreamRemoteSubmix(StreamContext* context, const Metadata& metadata,
-                                       const AudioDeviceAddress& deviceAddress)
+StreamRemoteSubmix::StreamRemoteSubmix(StreamContext* context, const Metadata& metadata)
     : StreamCommonImpl(context, metadata),
-      mDeviceAddress(deviceAddress),
-      mIsInput(isInput(metadata)) {
-    mStreamConfig.frameSize = context->getFrameSize();
-    mStreamConfig.format = context->getFormat();
-    mStreamConfig.channelLayout = context->getChannelLayout();
-    mStreamConfig.sampleRate = context->getSampleRate();
-}
+      mIsInput(isInput(metadata)),
+      mStreamConfig{.sampleRate = context->getSampleRate(),
+                    .format = context->getFormat(),
+                    .channelLayout = context->getChannelLayout(),
+                    .frameSize = context->getFrameSize()} {}
 
 StreamRemoteSubmix::~StreamRemoteSubmix() {
     cleanupWorker();
 }
 
 ::android::status_t StreamRemoteSubmix::init() {
-    mCurrentRoute = SubmixRoute::findOrCreateRoute(mDeviceAddress, mStreamConfig);
-    if (mCurrentRoute == nullptr) {
-        return ::android::NO_INIT;
-    }
-    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 ((!mIsInput || mCurrentRoute->isStreamInOpen()) && sink->isShutdown()) {
-        LOG(DEBUG) << __func__ << ": Shut down sink when opening stream";
-        if (::android::OK != mCurrentRoute->resetPipe()) {
-            LOG(ERROR) << __func__ << ": reset pipe failed";
-            return ::android::NO_INIT;
-        }
-    }
-    mCurrentRoute->openStream(mIsInput);
     return ::android::OK;
 }
 
 ::android::status_t StreamRemoteSubmix::drain(StreamDescriptor::DrainMode) {
-    usleep(1000);
     return ::android::OK;
 }
 
 ::android::status_t StreamRemoteSubmix::flush() {
-    usleep(1000);
     return ::android::OK;
 }
 
 ::android::status_t StreamRemoteSubmix::pause() {
-    usleep(1000);
     return ::android::OK;
 }
 
 ::android::status_t StreamRemoteSubmix::standby() {
-    mCurrentRoute->standby(mIsInput);
+    if (mCurrentRoute) mCurrentRoute->standby(mIsInput);
     return ::android::OK;
 }
 
 ::android::status_t StreamRemoteSubmix::start() {
-    mCurrentRoute->exitStandby(mIsInput);
+    if (mDeviceAddressUpdated.load(std::memory_order_acquire)) {
+        LOG(DEBUG) << __func__ << ": device address updated, reset current route";
+        shutdown();
+        mDeviceAddressUpdated.store(false, std::memory_order_release);
+    }
+    if (!mCurrentRoute) {
+        RETURN_STATUS_IF_ERROR(setCurrentRoute());
+        LOG(DEBUG) << __func__ << ": have current route? " << (mCurrentRoute != nullptr);
+    }
+    if (mCurrentRoute) mCurrentRoute->exitStandby(mIsInput);
     mStartTimeNs = ::android::uptimeNanos();
     mFramesSinceStart = 0;
     return ::android::OK;
 }
 
-ndk::ScopedAStatus StreamRemoteSubmix::prepareToClose() {
-    if (!mIsInput) {
-        std::shared_ptr<SubmixRoute> route = SubmixRoute::findRoute(mDeviceAddress);
-        if (route != nullptr) {
-            sp<MonoPipe> sink = route->getSink();
-            if (sink == nullptr) {
-                ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-            }
-            LOG(DEBUG) << __func__ << ": shutting down MonoPipe sink";
-
-            sink->shutdown(true);
-            // The client already considers this stream as closed, release the output end.
-            route->closeStream(mIsInput);
-        } else {
-            LOG(DEBUG) << __func__ << ": stream already closed.";
-            ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-        }
-    }
-    return ndk::ScopedAStatus::ok();
-}
-
 // Remove references to the specified input and output streams.  When the device no longer
 // references input and output streams destroy the associated pipe.
 void StreamRemoteSubmix::shutdown() {
+    if (!mCurrentRoute) return;
     mCurrentRoute->closeStream(mIsInput);
     // If all stream instances are closed, we can remove route information for this port.
     if (!mCurrentRoute->hasAtleastOneStreamOpen()) {
         mCurrentRoute->releasePipe();
         LOG(DEBUG) << __func__ << ": pipe destroyed";
-        SubmixRoute::removeRoute(mDeviceAddress);
+        SubmixRoute::removeRoute(getDeviceAddress());
     }
     mCurrentRoute.reset();
 }
 
 ::android::status_t StreamRemoteSubmix::transfer(void* buffer, size_t frameCount,
                                                  size_t* actualFrameCount, int32_t* latencyMs) {
+    if (mDeviceAddressUpdated.load(std::memory_order_acquire)) {
+        // 'setConnectedDevices' was called. I/O will be restarted.
+        return ::android::OK;
+    }
+
     *latencyMs = getDelayInUsForFrameCount(getStreamPipeSizeInFrames()) / 1000;
     LOG(VERBOSE) << __func__ << ": Latency " << *latencyMs << "ms";
-    mCurrentRoute->exitStandby(mIsInput);
-    ::android::status_t status = mIsInput ? inRead(buffer, frameCount, actualFrameCount)
-                                          : outWrite(buffer, frameCount, actualFrameCount);
-    if ((status != ::android::OK && mIsInput) ||
-        ((status != ::android::OK && status != ::android::DEAD_OBJECT) && !mIsInput)) {
-        return status;
+    ::android::status_t status = ::android::OK;
+    if (mCurrentRoute) {
+        mCurrentRoute->exitStandby(mIsInput);
+        status = mIsInput ? inRead(buffer, frameCount, actualFrameCount)
+                          : outWrite(buffer, frameCount, actualFrameCount);
+        if ((status != ::android::OK && mIsInput) ||
+            ((status != ::android::OK && status != ::android::DEAD_OBJECT) && !mIsInput)) {
+            return status;
+        }
+    } else {
+        LOG(WARNING) << __func__ << ": no current route";
+        if (mIsInput) {
+            memset(buffer, 0, mStreamConfig.frameSize * frameCount);
+        }
+        *actualFrameCount = frameCount;
     }
     mFramesSinceStart += *actualFrameCount;
-    if (!mIsInput && status != ::android::DEAD_OBJECT) return ::android::OK;
-    // Input streams always need to block, output streams need to block when there is no sink.
-    // When the sink exists, more sophisticated blocking algorithm is implemented by MonoPipe.
+    // If there is no route, always block, otherwise:
+    //  - Input streams always need to block, output streams need to block when there is no sink.
+    //  - When the sink exists, more sophisticated blocking algorithm is implemented by MonoPipe.
+    if (mCurrentRoute && !mIsInput && status != ::android::DEAD_OBJECT) return ::android::OK;
     const long bufferDurationUs =
             (*actualFrameCount) * MICROS_PER_SECOND / mContext.getSampleRate();
     const auto totalDurationUs = (::android::uptimeNanos() - mStartTimeNs) / NANOS_PER_MICROSECOND;
@@ -163,6 +141,10 @@
 }
 
 ::android::status_t StreamRemoteSubmix::refinePosition(StreamDescriptor::Position* position) {
+    if (!mCurrentRoute) {
+        RETURN_STATUS_IF_ERROR(setCurrentRoute());
+        if (!mCurrentRoute) return ::android::OK;
+    }
     sp<MonoPipeReader> source = mCurrentRoute->getSource();
     if (source == nullptr) {
         return ::android::NO_INIT;
@@ -186,6 +168,7 @@
 
 // Calculate the maximum size of the pipe buffer in frames for the specified stream.
 size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
+    if (!mCurrentRoute) return r_submix::kDefaultPipeSizeInFrames;
     auto pipeConfig = mCurrentRoute->getPipeConfig();
     const size_t maxFrameSize = std::max(mStreamConfig.frameSize, pipeConfig.frameSize);
     return (pipeConfig.frameCount * pipeConfig.frameSize) / maxFrameSize;
@@ -209,7 +192,7 @@
     }
     mWriteShutdownCount = 0;
 
-    LOG(VERBOSE) << __func__ << ": " << mDeviceAddress.toString() << ", " << frameCount
+    LOG(VERBOSE) << __func__ << ": " << getDeviceAddress().toString() << ", " << frameCount
                  << " frames";
 
     const bool shouldBlockWrite = mCurrentRoute->shouldBlockWrite();
@@ -283,8 +266,9 @@
     }
     mReadErrorCount = 0;
 
-    LOG(VERBOSE) << __func__ << ": " << mDeviceAddress.toString() << ", " << frameCount
+    LOG(VERBOSE) << __func__ << ": " << getDeviceAddress().toString() << ", " << frameCount
                  << " frames";
+
     // read the data from the pipe
     char* buff = (char*)buffer;
     size_t actuallyRead = 0;
@@ -324,10 +308,91 @@
     return ::android::OK;
 }
 
+::android::status_t StreamRemoteSubmix::setCurrentRoute() {
+    const auto address = getDeviceAddress();
+    if (address == AudioDeviceAddress{}) {
+        return ::android::OK;
+    }
+    mCurrentRoute = SubmixRoute::findOrCreateRoute(address, mStreamConfig);
+    if (mCurrentRoute == nullptr) {
+        return ::android::NO_INIT;
+    }
+    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 ((!mIsInput || mCurrentRoute->isStreamInOpen()) && sink->isShutdown()) {
+        LOG(DEBUG) << __func__ << ": Shut down sink when opening stream";
+        if (::android::OK != mCurrentRoute->resetPipe()) {
+            LOG(ERROR) << __func__ << ": reset pipe failed";
+            return ::android::NO_INIT;
+        }
+    }
+    mCurrentRoute->openStream(mIsInput);
+    return ::android::OK;
+}
+
+ndk::ScopedAStatus StreamRemoteSubmix::prepareToClose() {
+    if (!mIsInput) {
+        const auto address = getDeviceAddress();
+        if (address == AudioDeviceAddress{}) return ndk::ScopedAStatus::ok();
+        std::shared_ptr<SubmixRoute> route = SubmixRoute::findRoute(address);
+        if (route != nullptr) {
+            sp<MonoPipe> sink = route->getSink();
+            if (sink == nullptr) {
+                ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+            }
+            LOG(DEBUG) << __func__ << ": shutting down MonoPipe sink";
+
+            sink->shutdown(true);
+            // The client already considers this stream as closed, release the output end.
+            route->closeStream(mIsInput);
+        } else {
+            LOG(DEBUG) << __func__ << ": stream already closed.";
+            ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+        }
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamRemoteSubmix::setConnectedDevices(const ConnectedDevices& devices) {
+    if (devices.size() > 1) {
+        LOG(ERROR) << __func__ << ": Only single device supported, got " << devices.size();
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+    AudioDeviceAddress newAddress;
+    if (!devices.empty()) {
+        if (auto deviceDesc = devices.front().type;
+            (mIsInput && deviceDesc.type != AudioDeviceType::IN_SUBMIX) ||
+            (!mIsInput && deviceDesc.type != AudioDeviceType::OUT_SUBMIX)) {
+            LOG(ERROR) << __func__ << ": Device type " << toString(deviceDesc.type)
+                       << " not supported";
+            return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+        }
+        newAddress = devices.front().address;
+        LOG(DEBUG) << __func__ << ": connected to " << newAddress.toString();
+    } else {
+        LOG(DEBUG) << __func__ << ": disconnected";
+    }
+    RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(devices));
+    std::lock_guard guard(mLock);
+    if (mDeviceAddress != newAddress) {
+        mDeviceAddress = newAddress;
+        mDeviceAddressUpdated.store(true, std::memory_order_release);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
 StreamInRemoteSubmix::StreamInRemoteSubmix(StreamContext&& context,
                                            const SinkMetadata& sinkMetadata,
                                            const std::vector<MicrophoneInfo>& microphones)
-    : StreamIn(std::move(context), microphones), StreamSwitcher(&mContextInstance, sinkMetadata) {}
+    : StreamIn(std::move(context), microphones),
+      StreamRemoteSubmix(&mContextInstance, sinkMetadata) {}
 
 ndk::ScopedAStatus StreamInRemoteSubmix::getActiveMicrophones(
         std::vector<MicrophoneDynamicInfo>* _aidl_return) {
@@ -336,66 +401,10 @@
     return ndk::ScopedAStatus::ok();
 }
 
-StreamSwitcher::DeviceSwitchBehavior StreamInRemoteSubmix::switchCurrentStream(
-        const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
-    // This implementation effectively postpones stream creation until
-    // receiving the first call to 'setConnectedDevices' with a non-empty list.
-    if (isStubStream()) {
-        if (devices.size() == 1) {
-            auto deviceDesc = devices.front().type;
-            if (deviceDesc.type ==
-                ::aidl::android::media::audio::common::AudioDeviceType::IN_SUBMIX) {
-                return DeviceSwitchBehavior::CREATE_NEW_STREAM;
-            }
-            LOG(ERROR) << __func__ << ": Device type " << toString(deviceDesc.type)
-                       << " not supported";
-        } else {
-            LOG(ERROR) << __func__ << ": Only single device supported.";
-        }
-        return DeviceSwitchBehavior::UNSUPPORTED_DEVICES;
-    }
-    return DeviceSwitchBehavior::USE_CURRENT_STREAM;
-}
-
-std::unique_ptr<StreamCommonInterfaceEx> StreamInRemoteSubmix::createNewStream(
-        const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
-        StreamContext* context, const Metadata& metadata) {
-    return std::unique_ptr<StreamCommonInterfaceEx>(
-            new InnerStreamWrapper<StreamRemoteSubmix>(context, metadata, devices.front().address));
-}
-
 StreamOutRemoteSubmix::StreamOutRemoteSubmix(StreamContext&& context,
                                              const SourceMetadata& sourceMetadata,
                                              const std::optional<AudioOffloadInfo>& offloadInfo)
     : StreamOut(std::move(context), offloadInfo),
-      StreamSwitcher(&mContextInstance, sourceMetadata) {}
-
-StreamSwitcher::DeviceSwitchBehavior StreamOutRemoteSubmix::switchCurrentStream(
-        const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
-    // This implementation effectively postpones stream creation until
-    // receiving the first call to 'setConnectedDevices' with a non-empty list.
-    if (isStubStream()) {
-        if (devices.size() == 1) {
-            auto deviceDesc = devices.front().type;
-            if (deviceDesc.type ==
-                ::aidl::android::media::audio::common::AudioDeviceType::OUT_SUBMIX) {
-                return DeviceSwitchBehavior::CREATE_NEW_STREAM;
-            }
-            LOG(ERROR) << __func__ << ": Device type " << toString(deviceDesc.type)
-                       << " not supported";
-        } else {
-            LOG(ERROR) << __func__ << ": Only single device supported.";
-        }
-        return DeviceSwitchBehavior::UNSUPPORTED_DEVICES;
-    }
-    return DeviceSwitchBehavior::USE_CURRENT_STREAM;
-}
-
-std::unique_ptr<StreamCommonInterfaceEx> StreamOutRemoteSubmix::createNewStream(
-        const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
-        StreamContext* context, const Metadata& metadata) {
-    return std::unique_ptr<StreamCommonInterfaceEx>(
-            new InnerStreamWrapper<StreamRemoteSubmix>(context, metadata, devices.front().address));
-}
+      StreamRemoteSubmix(&mContextInstance, sourceMetadata) {}
 
 }  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/r_submix/SubmixRoute.h b/audio/aidl/default/r_submix/SubmixRoute.h
index 5425f12..0097f39 100644
--- a/audio/aidl/default/r_submix/SubmixRoute.h
+++ b/audio/aidl/default/r_submix/SubmixRoute.h
@@ -25,10 +25,12 @@
 #include <media/nbaio/MonoPipe.h>
 #include <media/nbaio/MonoPipeReader.h>
 
+#include <Utils.h>
 #include <aidl/android/media/audio/common/AudioChannelLayout.h>
 #include <aidl/android/media/audio/common/AudioDeviceAddress.h>
 #include <aidl/android/media/audio/common/AudioFormatDescription.h>
 
+using aidl::android::hardware::audio::common::getFrameSizeInBytes;
 using aidl::android::media::audio::common::AudioChannelLayout;
 using aidl::android::media::audio::common::AudioFormatDescription;
 using aidl::android::media::audio::common::AudioFormatType;
@@ -56,8 +58,8 @@
     AudioChannelLayout channelLayout =
             AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
                     AudioChannelLayout::LAYOUT_STEREO);
-    size_t frameSize;
-    size_t frameCount;
+    size_t frameSize = getFrameSizeInBytes(format, channelLayout);
+    size_t frameCount = 0;
 };
 
 class SubmixRoute {
diff --git a/audio/aidl/default/stub/DriverStubImpl.cpp b/audio/aidl/default/stub/DriverStubImpl.cpp
new file mode 100644
index 0000000..beb0114
--- /dev/null
+++ b/audio/aidl/default/stub/DriverStubImpl.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cmath>
+
+#define LOG_TAG "AHAL_Stream"
+#include <android-base/logging.h>
+#include <audio_utils/clock.h>
+
+#include "core-impl/DriverStubImpl.h"
+
+namespace aidl::android::hardware::audio::core {
+
+DriverStubImpl::DriverStubImpl(const StreamContext& context)
+    : mBufferSizeFrames(context.getBufferSizeInFrames()),
+      mFrameSizeBytes(context.getFrameSize()),
+      mSampleRate(context.getSampleRate()),
+      mIsAsynchronous(!!context.getAsyncCallback()),
+      mIsInput(context.isInput()) {}
+
+::android::status_t DriverStubImpl::init() {
+    mIsInitialized = true;
+    return ::android::OK;
+}
+
+::android::status_t DriverStubImpl::drain(StreamDescriptor::DrainMode) {
+    if (!mIsInitialized) {
+        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
+    }
+    if (!mIsInput) {
+        if (!mIsAsynchronous) {
+            static constexpr float kMicrosPerSecond = MICROS_PER_SECOND;
+            const size_t delayUs = static_cast<size_t>(
+                    std::roundf(mBufferSizeFrames * kMicrosPerSecond / mSampleRate));
+            usleep(delayUs);
+        } else {
+            usleep(500);
+        }
+    }
+    return ::android::OK;
+}
+
+::android::status_t DriverStubImpl::flush() {
+    if (!mIsInitialized) {
+        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
+    }
+    return ::android::OK;
+}
+
+::android::status_t DriverStubImpl::pause() {
+    if (!mIsInitialized) {
+        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
+    }
+    return ::android::OK;
+}
+
+::android::status_t DriverStubImpl::standby() {
+    if (!mIsInitialized) {
+        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
+    }
+    mIsStandby = true;
+    return ::android::OK;
+}
+
+::android::status_t DriverStubImpl::start() {
+    if (!mIsInitialized) {
+        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
+    }
+    mIsStandby = false;
+    mStartTimeNs = ::android::uptimeNanos();
+    mFramesSinceStart = 0;
+    return ::android::OK;
+}
+
+::android::status_t DriverStubImpl::transfer(void* buffer, size_t frameCount,
+                                             size_t* actualFrameCount, int32_t*) {
+    if (!mIsInitialized) {
+        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
+    }
+    if (mIsStandby) {
+        LOG(FATAL) << __func__ << ": must not happen while in standby";
+    }
+    *actualFrameCount = frameCount;
+    if (mIsAsynchronous) {
+        usleep(500);
+    } else {
+        mFramesSinceStart += *actualFrameCount;
+        const long bufferDurationUs = (*actualFrameCount) * MICROS_PER_SECOND / mSampleRate;
+        const auto totalDurationUs =
+                (::android::uptimeNanos() - mStartTimeNs) / NANOS_PER_MICROSECOND;
+        const long totalOffsetUs =
+                mFramesSinceStart * MICROS_PER_SECOND / mSampleRate - 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);
+        }
+    }
+    if (mIsInput) {
+        uint8_t* byteBuffer = static_cast<uint8_t*>(buffer);
+        for (size_t i = 0; i < frameCount * mFrameSizeBytes; ++i) {
+            byteBuffer[i] = std::rand() % 255;
+        }
+    }
+    return ::android::OK;
+}
+
+void DriverStubImpl::shutdown() {
+    mIsInitialized = false;
+}
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/stub/StreamStub.cpp b/audio/aidl/default/stub/StreamStub.cpp
index a3d99a8..f6c87e1 100644
--- a/audio/aidl/default/stub/StreamStub.cpp
+++ b/audio/aidl/default/stub/StreamStub.cpp
@@ -32,110 +32,12 @@
 namespace aidl::android::hardware::audio::core {
 
 StreamStub::StreamStub(StreamContext* context, const Metadata& metadata)
-    : StreamCommonImpl(context, metadata),
-      mBufferSizeFrames(getContext().getBufferSizeInFrames()),
-      mFrameSizeBytes(getContext().getFrameSize()),
-      mSampleRate(getContext().getSampleRate()),
-      mIsAsynchronous(!!getContext().getAsyncCallback()),
-      mIsInput(isInput(metadata)) {}
+    : StreamCommonImpl(context, metadata), DriverStubImpl(getContext()) {}
 
 StreamStub::~StreamStub() {
     cleanupWorker();
 }
 
-::android::status_t StreamStub::init() {
-    mIsInitialized = true;
-    return ::android::OK;
-}
-
-::android::status_t StreamStub::drain(StreamDescriptor::DrainMode) {
-    if (!mIsInitialized) {
-        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
-    }
-    if (!mIsInput) {
-        if (!mIsAsynchronous) {
-            static constexpr float kMicrosPerSecond = MICROS_PER_SECOND;
-            const size_t delayUs = static_cast<size_t>(
-                    std::roundf(mBufferSizeFrames * kMicrosPerSecond / mSampleRate));
-            usleep(delayUs);
-        } else {
-            usleep(500);
-        }
-    }
-    return ::android::OK;
-}
-
-::android::status_t StreamStub::flush() {
-    if (!mIsInitialized) {
-        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
-    }
-    return ::android::OK;
-}
-
-::android::status_t StreamStub::pause() {
-    if (!mIsInitialized) {
-        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
-    }
-    return ::android::OK;
-}
-
-::android::status_t StreamStub::standby() {
-    if (!mIsInitialized) {
-        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
-    }
-    mIsStandby = true;
-    return ::android::OK;
-}
-
-::android::status_t StreamStub::start() {
-    if (!mIsInitialized) {
-        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
-    }
-    mIsStandby = false;
-    mStartTimeNs = ::android::uptimeNanos();
-    mFramesSinceStart = 0;
-    return ::android::OK;
-}
-
-::android::status_t StreamStub::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
-                                         int32_t*) {
-    if (!mIsInitialized) {
-        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
-    }
-    if (mIsStandby) {
-        LOG(FATAL) << __func__ << ": must not happen while in standby";
-    }
-    *actualFrameCount = frameCount;
-    if (mIsAsynchronous) {
-        usleep(500);
-    } else {
-        mFramesSinceStart += *actualFrameCount;
-        const long bufferDurationUs =
-                (*actualFrameCount) * MICROS_PER_SECOND / mContext.getSampleRate();
-        const auto totalDurationUs =
-                (::android::uptimeNanos() - mStartTimeNs) / NANOS_PER_MICROSECOND;
-        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);
-        }
-    }
-    if (mIsInput) {
-        uint8_t* byteBuffer = static_cast<uint8_t*>(buffer);
-        for (size_t i = 0; i < frameCount * mFrameSizeBytes; ++i) {
-            byteBuffer[i] = std::rand() % 255;
-        }
-    }
-    return ::android::OK;
-}
-
-void StreamStub::shutdown() {
-    mIsInitialized = false;
-}
-
 StreamInStub::StreamInStub(StreamContext&& context, const SinkMetadata& sinkMetadata,
                            const std::vector<MicrophoneInfo>& microphones)
     : StreamIn(std::move(context), microphones), StreamStub(&mContextInstance, sinkMetadata) {}
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index 2bf70c7..9fe5801 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -87,6 +87,7 @@
 using aidl::android::media::audio::common::AudioDeviceType;
 using aidl::android::media::audio::common::AudioDualMonoMode;
 using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::AudioGainConfig;
 using aidl::android::media::audio::common::AudioInputFlags;
 using aidl::android::media::audio::common::AudioIoFlags;
 using aidl::android::media::audio::common::AudioLatencyMode;
@@ -450,6 +451,7 @@
     // This is implemented by the 'StreamFixture' utility class.
     static constexpr int kNegativeTestBufferSizeFrames = 256;
     static constexpr int kDefaultLargeBufferSizeFrames = 48000;
+    static constexpr int32_t kAidlVersion3 = 3;
 
     void SetUpImpl(const std::string& moduleName, bool setUpDebug = true) {
         ASSERT_NO_FATAL_FAILURE(ConnectToService(moduleName, setUpDebug));
@@ -478,6 +480,7 @@
         if (setUpDebug) {
             ASSERT_NO_FATAL_FAILURE(SetUpDebug());
         }
+        ASSERT_TRUE(module->getInterfaceVersion(&aidlVersion).isOk());
     }
 
     void RestartService() {
@@ -490,6 +493,7 @@
         if (setUpDebug) {
             ASSERT_NO_FATAL_FAILURE(SetUpDebug());
         }
+        ASSERT_TRUE(module->getInterfaceVersion(&aidlVersion).isOk());
     }
 
     void SetUpDebug() {
@@ -577,6 +581,7 @@
     std::unique_ptr<WithDebugFlags> debug;
     std::vector<AudioPort> initialPorts;
     std::vector<AudioRoute> initialRoutes;
+    int32_t aidlVersion;
 };
 
 class WithDevicePortConnectedState {
@@ -1821,6 +1826,46 @@
     }
 }
 
+TEST_P(AudioCoreModule, SetAudioPortConfigInvalidPortAudioGain) {
+    if (aidlVersion < kAidlVersion3) {
+        GTEST_SKIP() << "Skip for audio HAL version lower than " << kAidlVersion3;
+    }
+    std::vector<AudioPort> ports;
+    ASSERT_IS_OK(module->getAudioPorts(&ports));
+    bool atLeastOnePortWithNonemptyGain = false;
+    for (const auto port : ports) {
+        AudioPortConfig portConfig;
+        portConfig.portId = port.id;
+        if (port.gains.empty()) {
+            continue;
+        }
+        atLeastOnePortWithNonemptyGain = true;
+        int index = 0;
+        ASSERT_NE(0, port.gains[index].stepValue) << "Invalid audio port config gain step 0";
+        portConfig.gain->index = index;
+        AudioGainConfig invalidGainConfig;
+
+        int invalidGain = port.gains[index].maxValue + port.gains[index].stepValue;
+        invalidGainConfig.values.push_back(invalidGain);
+        portConfig.gain.emplace(invalidGainConfig);
+        bool applied = true;
+        AudioPortConfig suggestedConfig;
+        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT,
+                      module->setAudioPortConfig(portConfig, &suggestedConfig, &applied))
+                << "invalid port gain " << invalidGain << " lower than min gain";
+
+        invalidGain = port.gains[index].minValue - port.gains[index].stepValue;
+        invalidGainConfig.values[0] = invalidGain;
+        portConfig.gain.emplace(invalidGainConfig);
+        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT,
+                      module->setAudioPortConfig(portConfig, &suggestedConfig, &applied))
+                << "invalid port gain " << invalidGain << "higher than max gain";
+    }
+    if (!atLeastOnePortWithNonemptyGain) {
+        GTEST_SKIP() << "No audio port contains non-empty gain configuration";
+    }
+}
+
 TEST_P(AudioCoreModule, TryConnectMissingDevice) {
     // Limit checks to connection types that are known to be detectable by HAL implementations.
     static const std::set<std::string> kCheckedConnectionTypes{
diff --git a/authsecret/aidl/Android.bp b/authsecret/aidl/Android.bp
index 90e128d..b5e4e3d 100644
--- a/authsecret/aidl/Android.bp
+++ b/authsecret/aidl/Android.bp
@@ -10,6 +10,7 @@
 aidl_interface {
     name: "android.hardware.authsecret",
     vendor_available: true,
+    frozen: true,
     srcs: ["android/hardware/authsecret/*.aidl"],
     stability: "vintf",
     backend: {
diff --git a/automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceService.cpp b/automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceService.cpp
index 81f18b2..4166fdc 100644
--- a/automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceService.cpp
+++ b/automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceService.cpp
@@ -48,7 +48,8 @@
 }
 
 bool IvnAndroidDeviceService::init() {
-    std::ifstream configStream(mConfigPath);
+    std::string configPathStr(mConfigPath);
+    std::ifstream configStream(configPathStr);
     if (!configStream) {
         LOG(ERROR) << "couldn't open " << mConfigPath << " for parsing.";
         return false;
diff --git a/automotive/occupant_awareness/aidl/Android.bp b/automotive/occupant_awareness/aidl/Android.bp
index 1a8124c..33406ba 100644
--- a/automotive/occupant_awareness/aidl/Android.bp
+++ b/automotive/occupant_awareness/aidl/Android.bp
@@ -14,6 +14,7 @@
         "android/hardware/automotive/occupant_awareness/*.aidl",
     ],
     stability: "vintf",
+    frozen: true,
     backend: {
         java: {
             sdk_version: "module_current",
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
index 6668d16..b301557 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -538,6 +538,9 @@
                        << getErrorMsg(writeResult);
             }
             break;
+        case toInt(VehicleApPowerStateReport::ON):
+            ALOGI("Received VehicleApPowerStateReport::ON, entering normal operating state");
+            break;
         default:
             ALOGE("Unknown VehicleApPowerStateReport: %d", state);
             break;
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
index a458c5b..c62784e 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
@@ -145,6 +145,13 @@
                   << toString(session_type_);
         return;
       }
+    } else if (session_type_ == SessionType::HFP_SOFTWARE_DECODING_DATAPATH ||
+               session_type_ == SessionType::HFP_SOFTWARE_ENCODING_DATAPATH) {
+      if (audio_config.getTag() != AudioConfiguration::pcmConfig) {
+        LOG(ERROR) << __func__ << " invalid audio config type for SessionType ="
+                   << toString(session_type_);
+        return;
+      }
     } else {
       LOG(ERROR) << __func__ << " invalid SessionType ="
                  << toString(session_type_);
@@ -166,6 +173,13 @@
                   << toString(session_type_);
         return;
       }
+    } else if (session_type_ == SessionType::HFP_SOFTWARE_DECODING_DATAPATH ||
+               session_type_ == SessionType::HFP_SOFTWARE_ENCODING_DATAPATH) {
+      if (audio_config.getTag() != AudioConfiguration::pcmConfig) {
+        LOG(ERROR) << __func__ << " invalid audio config type for SessionType ="
+                   << toString(session_type_);
+        return;
+      }
     } else {
       LOG(ERROR) << __func__
                  << " invalid SessionType =" << toString(session_type_);
@@ -604,7 +618,9 @@
   if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
       session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
       session_type_ == SessionType::A2DP_SOFTWARE_DECODING_DATAPATH ||
-      session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
+      session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
+      session_type_ == SessionType::HFP_SOFTWARE_ENCODING_DATAPATH ||
+      session_type_ == SessionType::HFP_SOFTWARE_DECODING_DATAPATH) {
     return false;
   }
 
@@ -629,7 +645,9 @@
   if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
       session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
       session_type_ == SessionType::A2DP_SOFTWARE_DECODING_DATAPATH ||
-      session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
+      session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
+      session_type_ == SessionType::HFP_SOFTWARE_ENCODING_DATAPATH ||
+      session_type_ == SessionType::HFP_SOFTWARE_DECODING_DATAPATH) {
     return false;
   }
 
diff --git a/boot/aidl/client/BootControlClient.cpp b/boot/aidl/client/BootControlClient.cpp
index 89258d2..dca98c6 100644
--- a/boot/aidl/client/BootControlClient.cpp
+++ b/boot/aidl/client/BootControlClient.cpp
@@ -18,6 +18,7 @@
 
 #include <aidl/android/hardware/boot/IBootControl.h>
 #include <android-base/logging.h>
+#include <android/binder_ibinder.h>
 #include <android/binder_manager.h>
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <android/hardware/boot/1.1/IBootControl.h>
@@ -65,63 +66,79 @@
     using IBootControl = ::aidl::android::hardware::boot::IBootControl;
 
   public:
-    BootControlClientAidl(std::shared_ptr<IBootControl> module) : module_(module) {}
+    explicit BootControlClientAidl(std::shared_ptr<IBootControl> module)
+        : module_(module),
+          boot_control_death_recipient(AIBinder_DeathRecipient_new(onBootControlServiceDied)) {
+        binder_status_t status = AIBinder_linkToDeath(module->asBinder().get(),
+                                                      boot_control_death_recipient, nullptr);
+        if (status != STATUS_OK) {
+            LOG(ERROR) << "Could not link to binder death";
+            return;
+        }
+    }
 
     BootControlVersion GetVersion() const override { return BootControlVersion::BOOTCTL_AIDL; }
 
-    ~BootControlClientAidl() = default;
-    virtual int32_t GetNumSlots() const {
+    ~BootControlClientAidl() {
+        if (boot_control_death_recipient) {
+            AIBinder_unlinkToDeath(module_->asBinder().get(), boot_control_death_recipient, this);
+        }
+    }
+
+    void onBootControlServiceDied() { LOG(ERROR) << "boot control service AIDL died"; }
+
+    int32_t GetNumSlots() const override {
         int32_t ret = -1;
         LOG_NDK_STATUS(module_->getNumberSlots(&ret));
         return ret;
     }
 
-    int32_t GetCurrentSlot() const {
+    int32_t GetCurrentSlot() const override {
         int32_t ret = -1;
         LOG_NDK_STATUS(module_->getCurrentSlot(&ret));
         return ret;
     }
-    MergeStatus getSnapshotMergeStatus() const {
+    MergeStatus getSnapshotMergeStatus() const override {
         MergeStatus status = MergeStatus::UNKNOWN;
         LOG_NDK_STATUS(module_->getSnapshotMergeStatus(&status));
         return status;
     }
-    std::string GetSuffix(int32_t slot) const {
+    std::string GetSuffix(int32_t slot) const override {
         std::string ret;
         const auto status = module_->getSuffix(slot, &ret);
         if (!status.isOk()) {
-            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")"
-                       << " failed " << status.getDescription();
+            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed "
+                       << status.getDescription();
             return {};
         }
         return ret;
     }
 
-    std::optional<bool> IsSlotBootable(int32_t slot) const {
+    std::optional<bool> IsSlotBootable(int32_t slot) const override {
         bool ret = false;
         const auto status = module_->isSlotBootable(slot, &ret);
         if (!status.isOk()) {
-            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")"
-                       << " failed " << status.getDescription();
+            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed "
+                       << status.getDescription();
             return {};
         }
         return ret;
     }
 
-    CommandResult MarkSlotUnbootable(int32_t slot) {
+    CommandResult MarkSlotUnbootable(int32_t slot) override {
         const auto status = module_->setSlotAsUnbootable(slot);
         if (!status.isOk()) {
-            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")"
-                       << " failed " << status.getDescription();
+            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed "
+                       << status.getDescription();
         }
         return {.success = status.isOk(), .errMsg = status.getDescription()};
     }
 
-    CommandResult SetActiveBootSlot(int slot) {
+    CommandResult SetActiveBootSlot(int slot) override {
         const auto status = module_->setActiveBootSlot(slot);
         if (!status.isOk()) {
-            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")"
-                       << " failed " << status.getDescription();
+            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed "
+                       << status.getDescription();
         }
         return {.success = status.isOk(), .errMsg = status.getDescription()};
     }
@@ -132,18 +149,18 @@
     }
 
     // Check if |slot| is marked boot successfully.
-    std::optional<bool> IsSlotMarkedSuccessful(int slot) const {
+    std::optional<bool> IsSlotMarkedSuccessful(int slot) const override {
         bool ret = false;
         const auto status = module_->isSlotMarkedSuccessful(slot, &ret);
         if (!status.isOk()) {
-            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")"
-                       << " failed " << status.getDescription();
+            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed "
+                       << status.getDescription();
             return {};
         }
         return ret;
     }
 
-    CommandResult MarkBootSuccessful() {
+    CommandResult MarkBootSuccessful() override {
         const auto status = module_->markBootSuccessful();
         if (!status.isOk()) {
             LOG(ERROR) << __FUNCTION__ << " failed " << status.getDescription();
@@ -151,17 +168,23 @@
         return {.success = status.isOk(), .errMsg = status.getDescription()};
     }
 
-    CommandResult SetSnapshotMergeStatus(aidl::android::hardware::boot::MergeStatus merge_status) {
+    CommandResult SetSnapshotMergeStatus(
+            aidl::android::hardware::boot::MergeStatus merge_status) override {
         const auto status = module_->setSnapshotMergeStatus(merge_status);
         if (!status.isOk()) {
-            LOG(ERROR) << __FUNCTION__ << "(" << merge_status << ")"
-                       << " failed " << status.getDescription();
+            LOG(ERROR) << __FUNCTION__ << "(" << merge_status << ")" << " failed "
+                       << status.getDescription();
         }
         return {.success = status.isOk(), .errMsg = status.getDescription()};
     }
 
   private:
     const std::shared_ptr<IBootControl> module_;
+    AIBinder_DeathRecipient* boot_control_death_recipient;
+    static void onBootControlServiceDied(void* client) {
+        BootControlClientAidl* self = static_cast<BootControlClientAidl*>(client);
+        self->onBootControlServiceDied();
+    }
 };
 
 using namespace android::hardware::boot;
@@ -183,7 +206,7 @@
             return BootControlVersion::BOOTCTL_V1_0;
         }
     }
-    int32_t GetNumSlots() const {
+    int32_t GetNumSlots() const override {
         const auto ret = module_v1_->getNumberSlots();
         if (!ret.isOk()) {
             LOG(ERROR) << __FUNCTION__ << " failed " << ret.description();
@@ -191,7 +214,7 @@
         return ret.withDefault(-1);
     }
 
-    int32_t GetCurrentSlot() const {
+    int32_t GetCurrentSlot() const override {
         const auto ret = module_v1_->getCurrentSlot();
         if (!ret.isOk()) {
             LOG(ERROR) << __FUNCTION__ << " failed " << ret.description();
@@ -199,23 +222,21 @@
         return ret.withDefault(-1);
     }
 
-    std::string GetSuffix(int32_t slot) const {
+    std::string GetSuffix(int32_t slot) const override {
         std::string suffix;
         const auto ret = module_v1_->getSuffix(
                 slot,
                 [&](const ::android::hardware::hidl_string& slotSuffix) { suffix = slotSuffix; });
         if (!ret.isOk()) {
-            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")"
-                       << " failed " << ret.description();
+            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << ret.description();
         }
         return suffix;
     }
 
-    std::optional<bool> IsSlotBootable(int32_t slot) const {
+    std::optional<bool> IsSlotBootable(int32_t slot) const override {
         const auto ret = module_v1_->isSlotBootable(slot);
         if (!ret.isOk()) {
-            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")"
-                       << " failed " << ret.description();
+            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << ret.description();
             return {};
         }
         const auto bool_result = ret.withDefault(V1_0::BoolResult::INVALID_SLOT);
@@ -225,7 +246,7 @@
         return bool_result == V1_0::BoolResult::TRUE;
     }
 
-    CommandResult MarkSlotUnbootable(int32_t slot) {
+    CommandResult MarkSlotUnbootable(int32_t slot) override {
         CommandResult result;
         const auto ret =
                 module_v1_->setSlotAsUnbootable(slot, [&](const V1_0::CommandResult& error) {
@@ -233,26 +254,24 @@
                     result.errMsg = error.errMsg;
                 });
         if (!ret.isOk()) {
-            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")"
-                       << " failed " << ret.description();
+            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << ret.description();
         }
         return result;
     }
 
-    CommandResult SetActiveBootSlot(int32_t slot) {
+    CommandResult SetActiveBootSlot(int32_t slot) override {
         CommandResult result;
         const auto ret = module_v1_->setActiveBootSlot(slot, [&](const V1_0::CommandResult& error) {
             result.success = error.success;
             result.errMsg = error.errMsg;
         });
         if (!ret.isOk()) {
-            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")"
-                       << " failed " << ret.description();
+            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << ret.description();
         }
         return result;
     }
 
-    CommandResult MarkBootSuccessful() {
+    CommandResult MarkBootSuccessful() override {
         CommandResult result;
         const auto ret = module_v1_->markBootSuccessful([&](const V1_0::CommandResult& error) {
             result.success = error.success;
@@ -264,11 +283,10 @@
         return result;
     }
 
-    std::optional<bool> IsSlotMarkedSuccessful(int32_t slot) const {
+    std::optional<bool> IsSlotMarkedSuccessful(int32_t slot) const override {
         const auto ret = module_v1_->isSlotMarkedSuccessful(slot);
         if (!ret.isOk()) {
-            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")"
-                       << " failed " << ret.description();
+            LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << ret.description();
             return {};
         }
         const auto bool_result = ret.withDefault(V1_0::BoolResult::INVALID_SLOT);
@@ -278,7 +296,7 @@
         return bool_result == V1_0::BoolResult::TRUE;
     }
 
-    MergeStatus getSnapshotMergeStatus() const {
+    MergeStatus getSnapshotMergeStatus() const override {
         if (module_v1_1_ == nullptr) {
             LOG(ERROR) << __FUNCTION__ << " is unsupported, requires at least boot v1.1";
             return MergeStatus::UNKNOWN;
@@ -291,7 +309,7 @@
                 ret.withDefault(static_cast<V1_1::MergeStatus>(MergeStatus::UNKNOWN)));
     }
 
-    CommandResult SetSnapshotMergeStatus(MergeStatus merge_status) {
+    CommandResult SetSnapshotMergeStatus(MergeStatus merge_status) override {
         if (module_v1_1_ == nullptr) {
             return {.success = false,
                     .errMsg = "setSnapshotMergeStatus is unsupported, requires at least boot v1.1"};
@@ -299,13 +317,13 @@
         const auto ret =
                 module_v1_1_->setSnapshotMergeStatus(static_cast<V1_1::MergeStatus>(merge_status));
         if (!ret.isOk()) {
-            LOG(ERROR) << __FUNCTION__ << "(" << merge_status << ")"
-                       << " failed " << ret.description();
+            LOG(ERROR) << __FUNCTION__ << "(" << merge_status << ")" << " failed "
+                       << ret.description();
         }
         return {.success = ret.isOk(), .errMsg = ret.description()};
     }
 
-    int32_t GetActiveBootSlot() const {
+    int32_t GetActiveBootSlot() const override {
         if (module_v1_2_ == nullptr) {
             LOG(ERROR) << __FUNCTION__ << " is unsupported, requires at least boot v1.2";
             return -1;
@@ -326,7 +344,6 @@
 std::unique_ptr<BootControlClient> BootControlClient::WaitForService() {
     const auto instance_name =
             std::string(::aidl::android::hardware::boot::IBootControl::descriptor) + "/default";
-
     if (AServiceManager_isDeclared(instance_name.c_str())) {
         auto module = ::aidl::android::hardware::boot::IBootControl::fromBinder(
                 ndk::SpAIBinder(AServiceManager_waitForService(instance_name.c_str())));
diff --git a/camera/device/default/ExternalCameraDeviceSession.cpp b/camera/device/default/ExternalCameraDeviceSession.cpp
index 91196d4..abd5d7e 100644
--- a/camera/device/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/default/ExternalCameraDeviceSession.cpp
@@ -1136,6 +1136,11 @@
 
     uint32_t v4lBufferCount = (fps >= kDefaultFps) ? mCfg.numVideoBuffers : mCfg.numStillBuffers;
 
+    // Double the max lag in theory.
+    mMaxLagNs = v4lBufferCount * 1000000000LL * 2 / fps;
+    ALOGI("%s: set mMaxLagNs to %" PRIu64 " ns, v4lBufferCount %u", __FUNCTION__, mMaxLagNs,
+          v4lBufferCount);
+
     // VIDIOC_REQBUFS: create buffers
     v4l2_requestbuffers req_buffers{};
     req_buffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -1232,40 +1237,67 @@
         }
     }
 
-    ATRACE_BEGIN("VIDIOC_DQBUF");
+    uint64_t lagNs = 0;
     v4l2_buffer buffer{};
-    buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-    buffer.memory = V4L2_MEMORY_MMAP;
-    if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_DQBUF, &buffer)) < 0) {
-        ALOGE("%s: DQBUF fails: %s", __FUNCTION__, strerror(errno));
-        return ret;
-    }
-    ATRACE_END();
+    do {
+        ATRACE_BEGIN("VIDIOC_DQBUF");
+        buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+        buffer.memory = V4L2_MEMORY_MMAP;
+        if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_DQBUF, &buffer)) < 0) {
+            ALOGE("%s: DQBUF fails: %s", __FUNCTION__, strerror(errno));
+            return ret;
+        }
+        ATRACE_END();
 
-    if (buffer.index >= mV4L2BufferCount) {
-        ALOGE("%s: Invalid buffer id: %d", __FUNCTION__, buffer.index);
-        return ret;
-    }
+        if (buffer.index >= mV4L2BufferCount) {
+            ALOGE("%s: Invalid buffer id: %d", __FUNCTION__, buffer.index);
+            return ret;
+        }
 
-    if (buffer.flags & V4L2_BUF_FLAG_ERROR) {
-        ALOGE("%s: v4l2 buf error! buf flag 0x%x", __FUNCTION__, buffer.flags);
-        // TODO: try to dequeue again
-    }
+        if (buffer.flags & V4L2_BUF_FLAG_ERROR) {
+            ALOGE("%s: v4l2 buf error! buf flag 0x%x", __FUNCTION__, buffer.flags);
+            // TODO: try to dequeue again
+        }
 
-    if (buffer.bytesused > mMaxV4L2BufferSize) {
-        ALOGE("%s: v4l2 buffer bytes used: %u maximum %u", __FUNCTION__, buffer.bytesused,
-              mMaxV4L2BufferSize);
-        return ret;
-    }
+        if (buffer.bytesused > mMaxV4L2BufferSize) {
+            ALOGE("%s: v4l2 buffer bytes used: %u maximum %u", __FUNCTION__, buffer.bytesused,
+                  mMaxV4L2BufferSize);
+            return ret;
+        }
 
-    if (buffer.flags & V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) {
-        // Ideally we should also check for V4L2_BUF_FLAG_TSTAMP_SRC_SOE, but
-        // even V4L2_BUF_FLAG_TSTAMP_SRC_EOF is better than capture a timestamp now
-        *shutterTs = static_cast<nsecs_t>(buffer.timestamp.tv_sec) * 1000000000LL +
-                     buffer.timestamp.tv_usec * 1000LL;
-    } else {
-        *shutterTs = systemTime(SYSTEM_TIME_MONOTONIC);
-    }
+        nsecs_t curTimeNs = systemTime(SYSTEM_TIME_MONOTONIC);
+
+        if (buffer.flags & V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) {
+            // Ideally we should also check for V4L2_BUF_FLAG_TSTAMP_SRC_SOE, but
+            // even V4L2_BUF_FLAG_TSTAMP_SRC_EOF is better than capture a timestamp now
+            *shutterTs = static_cast<nsecs_t>(buffer.timestamp.tv_sec) * 1000000000LL +
+                         buffer.timestamp.tv_usec * 1000LL;
+        } else {
+            *shutterTs = curTimeNs;
+        }
+
+        // The tactic only takes effect on v4l2 buffers with flag V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC.
+        // Most USB cameras should have the feature.
+        if (curTimeNs < *shutterTs) {
+            lagNs = 0;
+            ALOGW("%s: should not happen, the monotonic clock has issue, shutterTs is in the "
+                  "future, curTimeNs %" PRId64 "  < "
+                  "shutterTs %" PRId64 "",
+                  __func__, curTimeNs, *shutterTs);
+        } else {
+            lagNs = curTimeNs - *shutterTs;
+        }
+
+        if (lagNs > mMaxLagNs) {
+            ALOGI("%s: drop too old buffer, index %d, lag %" PRIu64 " ns > max %" PRIu64 " ns", __FUNCTION__,
+                  buffer.index, lagNs, mMaxLagNs);
+            int retVal = ioctl(mV4l2Fd.get(), VIDIOC_QBUF, &buffer);
+            if (retVal) {
+                ALOGE("%s: unexpected VIDIOC_QBUF failed, retVal %d", __FUNCTION__, retVal);
+                return ret;
+            }
+        }
+    } while (lagNs > mMaxLagNs);
 
     {
         std::lock_guard<std::mutex> lk(mV4l2BufferLock);
diff --git a/camera/device/default/ExternalCameraDeviceSession.h b/camera/device/default/ExternalCameraDeviceSession.h
index 795b589..1c6ed06 100644
--- a/camera/device/default/ExternalCameraDeviceSession.h
+++ b/camera/device/default/ExternalCameraDeviceSession.h
@@ -382,6 +382,9 @@
     std::string mExifMake;
     std::string mExifModel;
     /* End of members not changed after initialize() */
+
+    // The max tolerant lag between the dequeued v4l2 buffer and current capture request.
+    uint64_t mMaxLagNs;
 };
 
 }  // namespace implementation
diff --git a/cas/1.2/default/Android.bp b/cas/1.2/default/Android.bp
index 38561fd..5bc8bad 100644
--- a/cas/1.2/default/Android.bp
+++ b/cas/1.2/default/Android.bp
@@ -46,6 +46,7 @@
     vintf_fragments: ["android.hardware.cas@1.2-service.xml"],
     defaults: ["cas_service_defaults@1.2"],
     init_rc: ["android.hardware.cas@1.2-service.rc"],
+    overrides: ["com.android.hardware.cas"],
 }
 
 cc_binary {
diff --git a/compatibility_matrices/Android.bp b/compatibility_matrices/Android.bp
index 6a3fa32..df5e5b1 100644
--- a/compatibility_matrices/Android.bp
+++ b/compatibility_matrices/Android.bp
@@ -21,6 +21,65 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
+// Device framework compatibility matrix (common to all FCM versions)
+// Reference: https://source.android.com/docs/core/architecture/vintf/comp-matrices
+vintf_compatibility_matrix {
+    name: "framework_compatibility_matrix.device.xml",
+    stem: "compatibility_matrix.device.xml",
+    type: "device_fcm",
+}
+
+// Phony target that installs all system compatibility matrix files
+SYSTEM_MATRIX_DEPS = [
+    "framework_compatibility_matrix.5.xml",
+    "framework_compatibility_matrix.6.xml",
+    "framework_compatibility_matrix.7.xml",
+    "framework_compatibility_matrix.8.xml",
+    "framework_compatibility_matrix.202404.xml",
+    "framework_compatibility_matrix.device.xml",
+]
+
+phony {
+    name: "system_compatibility_matrix.xml",
+    required: SYSTEM_MATRIX_DEPS,
+    product_variables: {
+        release_aidl_use_unfrozen: {
+            required: [
+                "framework_compatibility_matrix.202504.xml",
+            ],
+        },
+    },
+}
+
+// Product Compatibility Matrix
+vintf_compatibility_matrix {
+    name: "product_compatibility_matrix.xml",
+    stem: "compatibility_matrix.xml",
+    product_specific: true,
+    type: "product_fcm",
+}
+
+// Phony target that installs all framework compatibility matrix files (system + product)
+FRAMEWORK_MATRIX_DEPS = SYSTEM_MATRIX_DEPS + ["product_compatibility_matrix.xml"]
+
+phony {
+    name: "framework_compatibility_matrix.xml",
+    required: FRAMEWORK_MATRIX_DEPS,
+    product_variables: {
+        release_aidl_use_unfrozen: {
+            required: [
+                "framework_compatibility_matrix.202504.xml",
+            ],
+        },
+    },
+}
+
+////////////////////////////////////////////
+// AUTO GENERATED MODULES
+// DO NOT ADD MORE MODULES BELOW THIS LINE
+////////////////////////////////////////////
+
+// System compatibility matrices
 vintf_compatibility_matrix {
     name: "framework_compatibility_matrix.5.xml",
     stem: "compatibility_matrix.5.xml",
@@ -90,58 +149,4 @@
     kernel_configs: [
         "kernel_config_w_6.next",
     ],
-
-}
-
-// Device framework compatibility matrix (common to all FCM versions)
-// Reference: https://source.android.com/docs/core/architecture/vintf/comp-matrices
-vintf_compatibility_matrix {
-    name: "framework_compatibility_matrix.device.xml",
-    stem: "compatibility_matrix.device.xml",
-    type: "device_fcm",
-}
-
-// Phony target that installs all system compatibility matrix files
-SYSTEM_MATRIX_DEPS = [
-    "framework_compatibility_matrix.5.xml",
-    "framework_compatibility_matrix.6.xml",
-    "framework_compatibility_matrix.7.xml",
-    "framework_compatibility_matrix.8.xml",
-    "framework_compatibility_matrix.202404.xml",
-    "framework_compatibility_matrix.device.xml",
-]
-
-phony {
-    name: "system_compatibility_matrix.xml",
-    required: SYSTEM_MATRIX_DEPS,
-    product_variables: {
-        release_aidl_use_unfrozen: {
-            required: [
-                "framework_compatibility_matrix.202504.xml",
-            ],
-        },
-    },
-}
-
-// Product Compatibility Matrix
-vintf_compatibility_matrix {
-    name: "product_compatibility_matrix.xml",
-    stem: "compatibility_matrix.xml",
-    product_specific: true,
-    type: "product_fcm",
-}
-
-// Phony target that installs all framework compatibility matrix files (system + product)
-FRAMEWORK_MATRIX_DEPS = SYSTEM_MATRIX_DEPS + ["product_compatibility_matrix.xml"]
-
-phony {
-    name: "framework_compatibility_matrix.xml",
-    required: FRAMEWORK_MATRIX_DEPS,
-    product_variables: {
-        release_aidl_use_unfrozen: {
-            required: [
-                "framework_compatibility_matrix.202504.xml",
-            ],
-        },
-    },
 }
diff --git a/compatibility_matrices/bump.py b/compatibility_matrices/bump.py
index 4e3ceaa..ee2fa88 100755
--- a/compatibility_matrices/bump.py
+++ b/compatibility_matrices/bump.py
@@ -58,7 +58,6 @@
         self.bump_kernel_configs()
         self.copy_matrix()
         self.edit_android_bp()
-        self.edit_android_mk()
         self.bump_libvintf()
 
     def bump_kernel_configs(self):
@@ -127,28 +126,6 @@
         with open(android_bp, "w") as f:
             f.write("".join(lines))
 
-
-    # This Android.mk file may be deprecated soon and the functionality is
-    # replaced by the soong phony module system_compatibility_matrix.xml.
-    def edit_android_mk(self):
-        android_mk = self.interfaces_dir / "compatibility_matrices/Android.mk"
-        lines = []
-        with open(android_mk) as f:
-            if self.next_module_name in f.read():
-                return
-            f.seek(0)
-            for line in f:
-              if f"    {self.device_module_name} \\\n" in line:
-                  lines.append(f"    {self.current_module_name} \\\n")
-
-              if self.current_module_name in line:
-                  lines.append(f"    {self.next_module_name} \\\n")
-              else:
-                  lines.append(line)
-
-        with open(android_mk, "w") as f:
-            f.write("".join(lines))
-
     def bump_libvintf(self):
         if not self.current_version:
             print("Skip libvintf update...")
diff --git a/drm/aidl/Android.bp b/drm/aidl/Android.bp
index afcb603..c42e723 100644
--- a/drm/aidl/Android.bp
+++ b/drm/aidl/Android.bp
@@ -12,6 +12,7 @@
     vendor_available: true,
     srcs: ["android/hardware/drm/*.aidl"],
     stability: "vintf",
+    frozen: true,
     imports: [
         "android.hardware.common-V2",
     ],
diff --git a/dumpstate/aidl/Android.bp b/dumpstate/aidl/Android.bp
index 1eb8b32..45c992a 100644
--- a/dumpstate/aidl/Android.bp
+++ b/dumpstate/aidl/Android.bp
@@ -26,6 +26,7 @@
     vendor_available: true,
     srcs: ["android/hardware/dumpstate/*.aidl"],
     stability: "vintf",
+    frozen: true,
     backend: {
         cpp: {
             enabled: false,
diff --git a/gnss/aidl/vts/gnss_hal_test.cpp b/gnss/aidl/vts/gnss_hal_test.cpp
index 5e2cbe3..31e6536 100644
--- a/gnss/aidl/vts/gnss_hal_test.cpp
+++ b/gnss/aidl/vts/gnss_hal_test.cpp
@@ -462,6 +462,10 @@
         GnssData lastGnssData;
         ASSERT_TRUE(callback->gnss_data_cbq_.retrieve(lastGnssData, timeoutSeconds));
         EXPECT_EQ(callback->gnss_data_cbq_.calledCount(), i + 1);
+        if (i <= 2 && lastGnssData.measurements.size() == 0) {
+            // Allow 3 seconds tolerance for empty measurement
+            continue;
+        }
         ASSERT_TRUE(lastGnssData.measurements.size() > 0);
 
         // Validity check GnssData fields
@@ -507,6 +511,10 @@
         GnssData lastGnssData;
         ASSERT_TRUE(callback->gnss_data_cbq_.retrieve(lastGnssData, timeoutSeconds));
         EXPECT_EQ(callback->gnss_data_cbq_.calledCount(), i + 1);
+        if (i <= 2 && lastGnssData.measurements.size() == 0) {
+            // Allow 3 seconds tolerance to report empty measurement
+            continue;
+        }
         ASSERT_TRUE(lastGnssData.measurements.size() > 0);
 
         // Validity check GnssData fields
diff --git a/gnss/aidl/vts/gnss_hal_test_cases.cpp b/gnss/aidl/vts/gnss_hal_test_cases.cpp
index f7408d8..8abf7ab 100644
--- a/gnss/aidl/vts/gnss_hal_test_cases.cpp
+++ b/gnss/aidl/vts/gnss_hal_test_cases.cpp
@@ -419,6 +419,10 @@
         ASSERT_TRUE(callback->gnss_data_cbq_.retrieve(lastMeasurement,
                                                       kFirstGnssMeasurementTimeoutSeconds));
         EXPECT_EQ(callback->gnss_data_cbq_.calledCount(), i + 1);
+        if (i <= 2 && lastMeasurement.measurements.size() == 0) {
+            // Allow 3 seconds tolerance for empty measurement
+            continue;
+        }
         ASSERT_TRUE(lastMeasurement.measurements.size() > 0);
 
         // Validity check GnssData fields
@@ -479,6 +483,10 @@
         ASSERT_TRUE(callback->gnss_data_cbq_.retrieve(lastMeasurement,
                                                       kFirstGnssMeasurementTimeoutSeconds));
         EXPECT_EQ(callback->gnss_data_cbq_.calledCount(), i + 1);
+        if (i <= 2 && lastMeasurement.measurements.size() == 0) {
+            // Allow 3 seconds tolerance for empty measurement
+            continue;
+        }
         ASSERT_TRUE(lastMeasurement.measurements.size() > 0);
 
         // Validity check GnssData fields
@@ -1335,7 +1343,10 @@
         ASSERT_TRUE(callback->gnss_data_cbq_.retrieve(lastMeasurement,
                                                       kFirstGnssMeasurementTimeoutSeconds));
         EXPECT_EQ(callback->gnss_data_cbq_.calledCount(), i + 1);
-        ASSERT_TRUE(lastMeasurement.measurements.size() > 0);
+        if (i > 2) {
+            // Allow 3 seconds tolerance for empty measurement
+            ASSERT_TRUE(lastMeasurement.measurements.size() > 0);
+        }
 
         // Validity check GnssData fields
         checkGnssMeasurementClockFields(lastMeasurement);
@@ -1790,6 +1801,10 @@
         GnssData lastGnssData;
         ASSERT_TRUE(callback->gnss_data_cbq_.retrieve(lastGnssData, 10));
         EXPECT_EQ(callback->gnss_data_cbq_.calledCount(), i + 1);
+        if (i <= 2 && lastGnssData.measurements.size() == 0) {
+            // Allow 3 seconds tolerance to report empty measurement
+            continue;
+        }
         ASSERT_TRUE(lastGnssData.measurements.size() > 0);
 
         // Validity check GnssData fields
diff --git a/graphics/composer/2.2/default/Android.bp b/graphics/composer/2.2/default/Android.bp
new file mode 100644
index 0000000..5753bb0
--- /dev/null
+++ b/graphics/composer/2.2/default/Android.bp
@@ -0,0 +1,51 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "hardware_interfaces_license",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.graphics.composer@2.2-service",
+    vendor: true,
+    relative_install_path: "hw",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-DLOG_TAG=\"ComposerHal\"",
+    ],
+    srcs: ["service.cpp"],
+    init_rc: ["android.hardware.graphics.composer@2.2-service.rc"],
+    header_libs: ["android.hardware.graphics.composer@2.2-passthrough"],
+    shared_libs: [
+        "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.composer@2.2",
+        "android.hardware.graphics.composer@2.1-resources",
+        "android.hardware.graphics.composer@2.2-resources",
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "libfmq",
+        "libhardware",
+        "libhidlbase",
+        "libhwc2on1adapter",
+        "libhwc2onfbadapter",
+        "liblog",
+        "libsync",
+        "libutils",
+    ],
+}
diff --git a/graphics/composer/2.2/default/Android.mk b/graphics/composer/2.2/default/Android.mk
deleted file mode 100644
index 6f7ef85..0000000
--- a/graphics/composer/2.2/default/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := android.hardware.graphics.composer@2.2-service
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../NOTICE
-LOCAL_VENDOR_MODULE := true
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_CFLAGS := -Wall -Werror -DLOG_TAG=\"ComposerHal\"
-LOCAL_SRC_FILES := service.cpp
-LOCAL_INIT_RC := android.hardware.graphics.composer@2.2-service.rc
-LOCAL_HEADER_LIBRARIES := android.hardware.graphics.composer@2.2-passthrough
-LOCAL_SHARED_LIBRARIES := \
-        android.hardware.graphics.composer@2.1 \
-        android.hardware.graphics.composer@2.2 \
-        android.hardware.graphics.composer@2.1-resources \
-        android.hardware.graphics.composer@2.2-resources \
-        libbase \
-        libbinder \
-        libcutils \
-        libfmq \
-        libhardware \
-        libhidlbase \
-        libhwc2on1adapter \
-        libhwc2onfbadapter \
-        liblog \
-        libsync \
-        libutils
-
-ifdef TARGET_USES_DISPLAY_RENDER_INTENTS
-LOCAL_CFLAGS += -DUSES_DISPLAY_RENDER_INTENTS
-endif
-
-include $(BUILD_EXECUTABLE)
diff --git a/graphics/composer/aidl/vts/RenderEngineVts.cpp b/graphics/composer/aidl/vts/RenderEngineVts.cpp
index 4e7f773..bc5eb6f 100644
--- a/graphics/composer/aidl/vts/RenderEngineVts.cpp
+++ b/graphics/composer/aidl/vts/RenderEngineVts.cpp
@@ -28,9 +28,7 @@
     mRenderEngine = ::android::renderengine::RenderEngine::create(args);
 }
 
-TestRenderEngine::~TestRenderEngine() {
-    mRenderEngine.release();
-}
+TestRenderEngine::~TestRenderEngine() {}
 
 void TestRenderEngine::setRenderLayers(std::vector<std::shared_ptr<TestLayer>> layers) {
     sort(layers.begin(), layers.end(),
diff --git a/health/storage/OWNERS b/health/storage/OWNERS
index 7af8d99..ec1fd8f 100644
--- a/health/storage/OWNERS
+++ b/health/storage/OWNERS
@@ -1,6 +1,9 @@
 # Bug component: 30545
 
+# Don't inherit owners from hardware/interfaces/health/
 set noparent
+# But inherit from hardware/interfaces
+include platform/hardware/interfaces:/OWNERS
 
 jaegeuk@google.com
 elsk@google.com
diff --git a/health/storage/aidl/Android.bp b/health/storage/aidl/Android.bp
index c614efb..44484d6 100644
--- a/health/storage/aidl/Android.bp
+++ b/health/storage/aidl/Android.bp
@@ -26,6 +26,7 @@
     vendor_available: true,
     srcs: ["android/hardware/health/storage/*.aidl"],
     stability: "vintf",
+    frozen: true,
     backend: {
         cpp: {
             enabled: false,
diff --git a/input/common/aidl/Android.bp b/input/common/aidl/Android.bp
index 0759d2e..8721e5d 100644
--- a/input/common/aidl/Android.bp
+++ b/input/common/aidl/Android.bp
@@ -14,6 +14,7 @@
     vendor_available: true,
     srcs: ["android/hardware/input/common/*.aidl"],
     stability: "vintf",
+    frozen: true,
     backend: {
         cpp: {
             enabled: false,
diff --git a/input/processor/aidl/Android.bp b/input/processor/aidl/Android.bp
index 68adf32..6855b08 100644
--- a/input/processor/aidl/Android.bp
+++ b/input/processor/aidl/Android.bp
@@ -12,6 +12,7 @@
     name: "android.hardware.input.processor",
     host_supported: true,
     vendor_available: true,
+    frozen: true,
     srcs: ["android/hardware/input/processor/*.aidl"],
     imports: [
         "android.hardware.input.common-V1",
diff --git a/ir/aidl/Android.bp b/ir/aidl/Android.bp
index 25f6c8f..c561c52 100644
--- a/ir/aidl/Android.bp
+++ b/ir/aidl/Android.bp
@@ -26,6 +26,7 @@
     vendor_available: true,
     srcs: ["android/hardware/ir/*.aidl"],
     stability: "vintf",
+    frozen: true,
     backend: {
         cpp: {
             enabled: false,
diff --git a/keymaster/4.0/vts/performance/Android.bp b/keymaster/4.0/vts/performance/Android.bp
index d7342ad..8f26871 100644
--- a/keymaster/4.0/vts/performance/Android.bp
+++ b/keymaster/4.0/vts/performance/Android.bp
@@ -33,6 +33,5 @@
         "android.hardware.keymaster@4.0",
         "libkeymaster4support",
         "libsoftkeymasterdevice",
-        "libchrome"
     ],
 }
diff --git a/keymaster/4.0/vts/performance/Benchmark.cpp b/keymaster/4.0/vts/performance/Benchmark.cpp
index e5fdff2..723b543 100644
--- a/keymaster/4.0/vts/performance/Benchmark.cpp
+++ b/keymaster/4.0/vts/performance/Benchmark.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "keymaster_benchmark"
 
+#include <getopt.h>
+
 #include <android/hardware/keymaster/4.0/IKeymasterDevice.h>
 #include <android/hardware/keymaster/4.0/types.h>
 #include <keymaster/keymaster_configuration.h>
@@ -36,8 +38,6 @@
 #include <benchmark/benchmark.h>
 #include <hidl/Status.h>
 
-#include <base/command_line.h>
-
 namespace android {
 namespace hardware {
 namespace keymaster {
@@ -700,14 +700,26 @@
 }  // namespace hardware
 }  // namespace android
 
+namespace {
+
+std::string ParseCommandLineFlags(int argc, char** argv) {
+    std::string service_name = "default";
+    static struct option long_options[] = {{"service_name", required_argument, 0, 's'},
+                                           {0, 0, 0, 0}};
+    int opt;
+    while ((opt = getopt_long(argc, argv, "s:", long_options, nullptr)) != -1) {
+        if (opt == 's') {
+            service_name = optarg;
+        }
+    }
+    return service_name;
+}
+
+}  // namespace
+
 int main(int argc, char** argv) {
     ::benchmark::Initialize(&argc, argv);
-    base::CommandLine::Init(argc, argv);
-    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-    auto service_name = command_line->GetSwitchValueASCII("service_name");
-    if (service_name.empty()) {
-        service_name = "default";
-    }
+    std::string service_name = ParseCommandLineFlags(argc, argv);
     android::hardware::keymaster::V4_0::test::keymaster =
             android::hardware::keymaster::V4_0::test::KeymasterWrapper::newInstance(service_name);
     if (!android::hardware::keymaster::V4_0::test::keymaster) {
diff --git a/light/aidl/Android.bp b/light/aidl/Android.bp
index c9fba95..142be6d 100644
--- a/light/aidl/Android.bp
+++ b/light/aidl/Android.bp
@@ -14,6 +14,7 @@
         "android/hardware/light/*.aidl",
     ],
     stability: "vintf",
+    frozen: true,
     backend: {
         java: {
             sdk_version: "module_current",
diff --git a/media/omx/1.0/vts/functional/store/VtsHalMediaOmxV1_0TargetStoreTest.cpp b/media/omx/1.0/vts/functional/store/VtsHalMediaOmxV1_0TargetStoreTest.cpp
index d80e651..2d34afe 100644
--- a/media/omx/1.0/vts/functional/store/VtsHalMediaOmxV1_0TargetStoreTest.cpp
+++ b/media/omx/1.0/vts/functional/store/VtsHalMediaOmxV1_0TargetStoreTest.cpp
@@ -93,9 +93,9 @@
 
 void validateAttributes(
         const std::map<const std::string, const testing::internal::RE>& knownPatterns,
-        const std::vector<const struct AttributePattern>& unknownPatterns,
+        const std::vector<struct AttributePattern>& unknownPatterns,
         hidl_vec<IOmxStore::Attribute> attributes) {
-    std::set<const std::string> attributeKeys;
+    std::set<std::string> attributeKeys;
     for (const auto& attr : attributes) {
         // Make sure there are no duplicates
         const auto [nodeIter, inserted] = attributeKeys.insert(attr.key);
@@ -179,7 +179,7 @@
          * tried for a match with the second element of the pair. If this second
          * match fails, the test will fail.
          */
-        const std::vector<const struct AttributePattern> unknownPatterns = {
+        const std::vector<struct AttributePattern> unknownPatterns = {
                 {"supports-[a-z0-9-]*", "0|1"}};
 
         validateAttributes(knownPatterns, unknownPatterns, attributes);
@@ -248,7 +248,7 @@
     };
 
     // Strings for matching rules for node attributes with key patterns
-    const std::vector<const struct AttributePattern> unknownPatterns = {
+    const std::vector<struct AttributePattern> unknownPatterns = {
             {"measured-frame-rate-" + size + "-range", range_num},
             {"feature-[a-zA-Z0-9_-]+", string},
     };
@@ -257,9 +257,9 @@
     const testing::internal::RE nodeNamePattern = "[a-zA-Z0-9._-]+";
     const testing::internal::RE nodeOwnerPattern = "[a-zA-Z0-9._-]+";
 
-    std::set<const std::string> roleKeys;
-    std::map<const std::string, std::set<const std::string>> nodeToRoles;
-    std::map<const std::string, std::set<const std::string>> ownerToNodes;
+    std::set<std::string> roleKeys;
+    std::map<const std::string, std::set<std::string>> nodeToRoles;
+    std::map<const std::string, std::set<std::string>> ownerToNodes;
     for (const IOmxStore::RoleInfo& role : roleList) {
         // Make sure there are no duplicates
         const auto [roleIter, inserted] = roleKeys.insert(role.role);
@@ -276,7 +276,7 @@
         }
 
         // Check the nodes for this role
-        std::set<const std::string> nodeKeys;
+        std::set<std::string> nodeKeys;
         for (const IOmxStore::NodeInfo& node : role.nodes) {
             // Make sure there are no duplicates
             const auto [nodeIter, inserted] = nodeKeys.insert(node.name);
@@ -317,7 +317,7 @@
 
         // Verify that roles for each node match with the information from
         // IOmxStore::listRoles().
-        std::set<const std::string> nodeKeys;
+        std::set<std::string> nodeKeys;
         for (IOmx::ComponentInfo node : nodeList) {
             // Make sure there are no duplicates
             const auto [nodeIter, inserted] = nodeKeys.insert(node.mName);
@@ -334,7 +334,7 @@
 
             // All the roles advertised by IOmxStore::listRoles() for this
             // node must be included in roleKeys.
-            std::set<const std::string> difference;
+            std::set<std::string> difference;
             std::set_difference(nodeToRoles[node.mName].begin(), nodeToRoles[node.mName].end(),
                                 roleKeys.begin(), roleKeys.end(),
                                 std::inserter(difference, difference.begin()));
@@ -347,7 +347,7 @@
         }
         // Check that all nodes obtained from IOmxStore::listRoles() are
         // supported by the their corresponding IOmx instances.
-        std::set<const std::string> difference;
+        std::set<std::string> difference;
         std::set_difference(nodes.begin(), nodes.end(), nodeKeys.begin(), nodeKeys.end(),
                             std::inserter(difference, difference.begin()));
         EXPECT_EQ(difference.empty(), true) << "IOmx::listNodes() for IOmx "
diff --git a/neuralnetworks/aidl/Android.bp b/neuralnetworks/aidl/Android.bp
index 145604c..c9242ca 100644
--- a/neuralnetworks/aidl/Android.bp
+++ b/neuralnetworks/aidl/Android.bp
@@ -11,6 +11,7 @@
     name: "android.hardware.neuralnetworks",
     host_supported: true,
     vendor_available: true,
+    frozen: true,
     srcs: [
         "android/hardware/neuralnetworks/*.aidl",
     ],
diff --git a/nfc/aidl/Android.bp b/nfc/aidl/Android.bp
index ae68f17..b34e4f2 100644
--- a/nfc/aidl/Android.bp
+++ b/nfc/aidl/Android.bp
@@ -27,6 +27,7 @@
     vendor_available: true,
     srcs: ["android/hardware/nfc/*.aidl"],
     stability: "vintf",
+    frozen: false,
     backend: {
         cpp: {
             enabled: false,
diff --git a/oemlock/aidl/Android.bp b/oemlock/aidl/Android.bp
index 1c19bb1..f4533ed 100644
--- a/oemlock/aidl/Android.bp
+++ b/oemlock/aidl/Android.bp
@@ -10,6 +10,7 @@
 aidl_interface {
     name: "android.hardware.oemlock",
     vendor_available: true,
+    frozen: true,
     srcs: ["android/hardware/oemlock/*.aidl"],
     stability: "vintf",
     backend: {
diff --git a/rebootescrow/aidl/Android.bp b/rebootescrow/aidl/Android.bp
index 39aaa07..3d5b827 100644
--- a/rebootescrow/aidl/Android.bp
+++ b/rebootescrow/aidl/Android.bp
@@ -10,6 +10,7 @@
 aidl_interface {
     name: "android.hardware.rebootescrow",
     vendor_available: true,
+    frozen: true,
     srcs: [
         "android/hardware/rebootescrow/IRebootEscrow.aidl",
     ],
diff --git a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
index f669110..9e3e159 100644
--- a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
+++ b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -253,39 +253,14 @@
 
     // Collection of valid attestation ID tags.
     auto attestation_id_tags = AuthorizationSetBuilder();
-    // Use ro.product.brand_for_attestation property for attestation if it is present else fallback
-    // to ro.product.brand
-    std::string prop_value =
-            ::android::base::GetProperty("ro.product.brand_for_attestation", /* default= */ "");
-    if (!prop_value.empty()) {
-        add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND,
-                          "ro.product.brand_for_attestation");
-    } else {
-        add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
-    }
-    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
-    // Use ro.product.name_for_attestation property for attestation if it is present else fallback
-    // to ro.product.name
-    prop_value = ::android::base::GetProperty("ro.product.name_for_attestation", /* default= */ "");
-    if (!prop_value.empty()) {
-        add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT,
-                          "ro.product.name_for_attestation");
-    } else {
-        add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
-    }
+
+    add_attestation_id(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND, "brand");
+    add_attestation_id(&attestation_id_tags, TAG_ATTESTATION_ID_DEVICE, "device");
+    add_attestation_id(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT, "name");
     add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serialno");
-    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MANUFACTURER,
-                      "ro.product.manufacturer");
-    // Use ro.product.model_for_attestation property for attestation if it is present else fallback
-    // to ro.product.model
-    prop_value =
-            ::android::base::GetProperty("ro.product.model_for_attestation", /* default= */ "");
-    if (!prop_value.empty()) {
-        add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL,
-                          "ro.product.model_for_attestation");
-    } else {
-        add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model");
-    }
+    add_attestation_id(&attestation_id_tags, TAG_ATTESTATION_ID_MANUFACTURER, "manufacturer");
+    add_attestation_id(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL, "model");
+
     vector<uint8_t> key_blob;
     vector<KeyCharacteristics> key_characteristics;
 
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index c19ab11..cfe9fa7 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -2400,6 +2400,43 @@
     return imei;
 }
 
+std::optional<std::string> get_attestation_id(const char* prop) {
+    // The frameworks code (in AndroidKeyStoreKeyPairGeneratorSpi.java) populates device ID
+    // values from one of 3 places, so the same logic needs to be reproduced here so the tests
+    // check what's expected correctly.
+    //
+    // In order of preference, the properties checked are:
+    //
+    // 1) `ro.product.<device-id>_for_attestation`: This should only be set in special cases; in
+    //     particular, AOSP builds for reference devices use a different value than the normal
+    //     builds for the same device (e.g. model of "aosp_raven" instead of "raven").
+    ::android::String8 prop_name =
+            ::android::String8::format("ro.product.%s_for_attestation", prop);
+    std::string prop_value = ::android::base::GetProperty(prop_name.c_str(), /* default= */ "");
+    if (!prop_value.empty()) {
+        return prop_value;
+    }
+
+    // 2) `ro.product.vendor.<device-id>`: This property refers to the vendor code, and so is
+    //    retained even in a GSI environment.
+    prop_name = ::android::String8::format("ro.product.vendor.%s", prop);
+    prop_value = ::android::base::GetProperty(prop_name.c_str(), /* default= */ "");
+    if (!prop_value.empty()) {
+        return prop_value;
+    }
+
+    // 3) `ro.product.<device-id>`: Note that this property is replaced by a default value when
+    //    running a GSI environment, and so will *not* match the value expected/used by the
+    //    vendor code on the device.
+    prop_name = ::android::String8::format("ro.product.%s", prop);
+    prop_value = ::android::base::GetProperty(prop_name.c_str(), /* default= */ "");
+    if (!prop_value.empty()) {
+        return prop_value;
+    }
+
+    return std::nullopt;
+}
+
 }  // namespace test
 
 }  // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index 0368bba..85ae93d 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <functional>
+#include <optional>
 #include <string_view>
 
 #include <aidl/Gtest.h>
@@ -384,14 +385,20 @@
                                     const string& plaintext, const string& exp_cipher_text);
 };
 
+// If the given string is non-empty, add it to the tag set under the given tag ID.
+template <Tag tag>
+void add_tag(AuthorizationSetBuilder* tags, TypedTag<TagType::BYTES, tag> ttag,
+             const std::string& prop_value) {
+    if (!prop_value.empty()) {
+        tags->Authorization(ttag, prop_value.data(), prop_value.size());
+    }
+}
+
 // If the given property is available, add it to the tag set under the given tag ID.
 template <Tag tag>
 void add_tag_from_prop(AuthorizationSetBuilder* tags, TypedTag<TagType::BYTES, tag> ttag,
                        const char* prop) {
-    std::string prop_value = ::android::base::GetProperty(prop, /* default= */ "");
-    if (!prop_value.empty()) {
-        tags->Authorization(ttag, prop_value.data(), prop_value.size());
-    }
+    add_tag(tags, ttag, ::android::base::GetProperty(prop, /* default= */ ""));
 }
 
 // Return the VSR API level for this device.
@@ -431,6 +438,20 @@
 std::optional<int32_t> keymint_feature_value(bool strongbox);
 std::string get_imei(int slot);
 
+// Retrieve a device ID property value, to match what is expected in attestations.
+std::optional<std::string> get_attestation_id(const char* prop);
+
+// Add the appropriate attestation device ID tag value to the provided `AuthorizationSetBuilder`,
+// if found.
+template <Tag tag>
+void add_attestation_id(AuthorizationSetBuilder* attestation_id_tags,
+                        TypedTag<TagType::BYTES, tag> tag_type, const char* prop) {
+    auto prop_value = get_attestation_id(prop);
+    if (prop_value.has_value()) {
+        add_tag(attestation_id_tags, tag_type, prop_value.value());
+    }
+}
+
 AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
 AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
 ::testing::AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain,
@@ -444,29 +465,6 @@
                              ::android::PrintInstanceNameToString);                  \
     GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name);
 
-// Use `ro.product.<property>_for_attestation` property for attestation if it is present else
-// fallback to use `ro.product.vendor.<property>` if it is present else fallback to
-// `ro.product.<property>`. Similar logic can be seen in Java method `getVendorDeviceIdProperty`
-// in frameworks/base/core/java/android/os/Build.java.
-template <Tag tag>
-void add_attestation_id(AuthorizationSetBuilder* attestation_id_tags,
-                        TypedTag<TagType::BYTES, tag> tag_type, const char* prop) {
-    ::android::String8 prop_name =
-            ::android::String8::format("ro.product.%s_for_attestation", prop);
-    std::string prop_value = ::android::base::GetProperty(prop_name.c_str(), /* default= */ "");
-    if (!prop_value.empty()) {
-        add_tag_from_prop(attestation_id_tags, tag_type, prop_name.c_str());
-    } else {
-        prop_name = ::android::String8::format("ro.product.vendor.%s", prop);
-        prop_value = ::android::base::GetProperty(prop_name.c_str(), /* default= */ "");
-        if (!prop_value.empty()) {
-            add_tag_from_prop(attestation_id_tags, tag_type, prop_name.c_str());
-        } else {
-            prop_name = ::android::String8::format("ro.product.%s", prop);
-            add_tag_from_prop(attestation_id_tags, tag_type, prop_name.c_str());
-        }
-    }
-}
 }  // namespace test
 
 }  // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp
index 5c9efef..608d328 100644
--- a/security/keymint/support/Android.bp
+++ b/security/keymint/support/Android.bp
@@ -40,6 +40,9 @@
     export_include_dirs: [
         "include",
     ],
+    header_libs: [
+        "libhardware_headers",
+    ],
     defaults: [
         "keymint_use_latest_hal_aidl_ndk_shared",
     ],
diff --git a/security/secureclock/aidl/Android.bp b/security/secureclock/aidl/Android.bp
index 853ad89..d7e7b43 100644
--- a/security/secureclock/aidl/Android.bp
+++ b/security/secureclock/aidl/Android.bp
@@ -10,6 +10,7 @@
 aidl_interface {
     name: "android.hardware.security.secureclock",
     vendor_available: true,
+    frozen: true,
     srcs: [
         "android/hardware/security/secureclock/*.aidl",
     ],
diff --git a/security/sharedsecret/aidl/Android.bp b/security/sharedsecret/aidl/Android.bp
index fe77c10..adf33d3 100644
--- a/security/sharedsecret/aidl/Android.bp
+++ b/security/sharedsecret/aidl/Android.bp
@@ -10,6 +10,7 @@
 aidl_interface {
     name: "android.hardware.security.sharedsecret",
     vendor_available: true,
+    frozen: true,
     srcs: [
         "android/hardware/security/sharedsecret/*.aidl",
     ],
diff --git a/soundtrigger/2.0/default/Android.bp b/soundtrigger/2.0/default/Android.bp
index 2cbf041..2e61f9b 100644
--- a/soundtrigger/2.0/default/Android.bp
+++ b/soundtrigger/2.0/default/Android.bp
@@ -46,3 +46,36 @@
         "libhardware_headers",
     ],
 }
+
+soong_config_module_type {
+    name: "soundtrigger_cc_library_shared",
+    module_type: "cc_library_shared",
+    config_namespace: "soundtrigger",
+    value_variables: [
+        "audioserver_multilib",
+    ],
+    properties: ["compile_multilib"],
+}
+
+soundtrigger_cc_library_shared {
+    name: "android.hardware.soundtrigger@2.0-impl",
+    vendor: true,
+    relative_install_path: "hw",
+    srcs: ["FetchISoundTriggerHw.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libhardware",
+        "libutils",
+        "android.hardware.soundtrigger@2.0",
+        "android.hardware.soundtrigger@2.0-core",
+    ],
+    compile_multilib: "32",
+    soong_config_variables: {
+        audioserver_multilib: {
+            compile_multilib: "%s",
+        },
+    },
+}
diff --git a/soundtrigger/2.0/default/Android.mk b/soundtrigger/2.0/default/Android.mk
deleted file mode 100644
index 17e4440..0000000
--- a/soundtrigger/2.0/default/Android.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# Copyright (C) 2016 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.
-
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := android.hardware.soundtrigger@2.0-impl
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
-LOCAL_VENDOR_MODULE := true
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SRC_FILES := \
-    FetchISoundTriggerHw.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
-        libhardware \
-        libutils \
-        android.hardware.soundtrigger@2.0 \
-        android.hardware.soundtrigger@2.0-core
-
-LOCAL_C_INCLUDE_DIRS := $(LOCAL_PATH)
-
-ifeq ($(strip $(AUDIOSERVER_MULTILIB)),)
-LOCAL_MULTILIB := 32
-else
-LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
-endif
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/soundtrigger/2.1/default/Android.bp b/soundtrigger/2.1/default/Android.bp
new file mode 100644
index 0000000..a246680
--- /dev/null
+++ b/soundtrigger/2.1/default/Android.bp
@@ -0,0 +1,57 @@
+//
+// Copyright (C) 2018 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 {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "hardware_interfaces_license",
+    ],
+}
+
+soong_config_module_type_import {
+    from: "hardware/interfaces/soundtrigger/2.0/default/Android.bp",
+    module_types: ["soundtrigger_cc_library_shared"],
+}
+
+soundtrigger_cc_library_shared {
+    name: "android.hardware.soundtrigger@2.1-impl",
+    vendor: true,
+    relative_install_path: "hw",
+    srcs: ["SoundTriggerHw.cpp"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "libhardware",
+        "libhidlbase",
+        "libhidlmemory",
+        "liblog",
+        "libutils",
+        "android.hardware.soundtrigger@2.1",
+        "android.hardware.soundtrigger@2.0",
+        "android.hardware.soundtrigger@2.0-core",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+    ],
+    compile_multilib: "32",
+    soong_config_variables: {
+        audioserver_multilib: {
+            compile_multilib: "%s",
+        },
+    },
+}
diff --git a/soundtrigger/2.1/default/Android.mk b/soundtrigger/2.1/default/Android.mk
deleted file mode 100644
index 602f5a7..0000000
--- a/soundtrigger/2.1/default/Android.mk
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# Copyright (C) 2018 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.
-
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := android.hardware.soundtrigger@2.1-impl
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
-LOCAL_VENDOR_MODULE := true
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SRC_FILES := \
-    SoundTriggerHw.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
-        libhardware \
-        libhidlbase \
-        libhidlmemory \
-        liblog \
-        libutils \
-        android.hardware.soundtrigger@2.1 \
-        android.hardware.soundtrigger@2.0 \
-        android.hardware.soundtrigger@2.0-core \
-        android.hidl.allocator@1.0 \
-        android.hidl.memory@1.0
-
-LOCAL_C_INCLUDE_DIRS := $(LOCAL_PATH)
-
-ifeq ($(strip $(AUDIOSERVER_MULTILIB)),)
-LOCAL_MULTILIB := 32
-else
-LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
-endif
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/threadnetwork/aidl/default/socket_interface.cpp b/threadnetwork/aidl/default/socket_interface.cpp
index a5aa2b4..71c6b4f 100644
--- a/threadnetwork/aidl/default/socket_interface.cpp
+++ b/threadnetwork/aidl/default/socket_interface.cpp
@@ -36,6 +36,7 @@
 #include <vector>
 
 #include "common/code_utils.hpp"
+#include "openthread/error.h"
 #include "openthread/openthread-system.h"
 #include "platform-posix.h"
 
@@ -56,14 +57,9 @@
 
 otError SocketInterface::Init(ReceiveFrameCallback aCallback, void* aCallbackContext,
                               RxFrameBuffer& aFrameBuffer) {
-    otError error = OT_ERROR_NONE;
+    otError error = InitSocket();
 
-    VerifyOrExit(mSockFd == -1, error = OT_ERROR_ALREADY);
-
-    WaitForSocketFileCreated(mRadioUrl.GetPath());
-
-    mSockFd = OpenFile(mRadioUrl);
-    VerifyOrExit(mSockFd != -1, error = OT_ERROR_FAILED);
+    VerifyOrExit(error == OT_ERROR_NONE);
 
     mReceiveFrameCallback = aCallback;
     mReceiveFrameContext = aCallbackContext;
@@ -155,9 +151,22 @@
 
     VerifyOrExit(!mIsHardwareResetting, error = OT_ERROR_FAILED);
 
-    WaitForSocketFileCreated(mRadioUrl.GetPath());
-    mSockFd = OpenFile(mRadioUrl);
-    VerifyOrExit(mSockFd != -1, error = OT_ERROR_FAILED);
+exit:
+    return error;
+}
+
+otError SocketInterface::InitSocket() {
+    otError error = OT_ERROR_NONE;
+    int retries = 0;
+
+    VerifyOrExit(mSockFd == -1, error = OT_ERROR_ALREADY);
+
+    while (retries++ < kMaxRetriesForSocketInit) {
+        WaitForSocketFileCreated(mRadioUrl.GetPath());
+        mSockFd = OpenFile(mRadioUrl);
+        VerifyOrExit(mSockFd == -1);
+    }
+    error = OT_ERROR_FAILED;
 
 exit:
     return error;
@@ -168,11 +177,16 @@
 
     assert(context != nullptr);
 
+    VerifyOrExit(mSockFd != -1);
+
     FD_SET(mSockFd, &context->mReadFdSet);
 
     if (context->mMaxFd < mSockFd) {
         context->mMaxFd = mSockFd;
     }
+
+exit:
+    return;
 }
 
 void SocketInterface::Process(const void* aMainloopContext) {
@@ -181,9 +195,14 @@
 
     assert(context != nullptr);
 
+    VerifyOrExit(mSockFd != -1);
+
     if (FD_ISSET(mSockFd, &context->mReadFdSet)) {
         Read();
     }
+
+exit:
+    return;
 }
 
 otError SocketInterface::HardwareReset(void) {
@@ -198,22 +217,24 @@
 
 void SocketInterface::Read(void) {
     uint8_t buffer[kMaxFrameSize];
+    ssize_t rval;
 
-    ssize_t rval = TEMP_FAILURE_RETRY(read(mSockFd, buffer, sizeof(buffer)));
+    VerifyOrExit(mSockFd != -1);
+
+    rval = TEMP_FAILURE_RETRY(read(mSockFd, buffer, sizeof(buffer)));
 
     if (rval > 0) {
         ProcessReceivedData(buffer, static_cast<uint16_t>(rval));
     } else if (rval < 0) {
         DieNow(OT_EXIT_ERROR_ERRNO);
     } else {
-        if (mIsHardwareResetting) {
-            LogInfo("Socket connection is closed due to hardware reset.");
-            ResetStates();
-        } else {
-            LogCrit("Socket connection is closed by remote.");
-            exit(OT_EXIT_FAILURE);
-        }
+        LogWarn("Socket connection is closed by remote, isHardwareReset: %d", mIsHardwareResetting);
+        ResetStates();
+        InitSocket();
     }
+
+exit:
+    return;
 }
 
 void SocketInterface::Write(const uint8_t* aFrame, uint16_t aLength) {
diff --git a/threadnetwork/aidl/default/socket_interface.hpp b/threadnetwork/aidl/default/socket_interface.hpp
index 494d76a..83c86e8 100644
--- a/threadnetwork/aidl/default/socket_interface.hpp
+++ b/threadnetwork/aidl/default/socket_interface.hpp
@@ -247,6 +247,15 @@
     otError WaitForHardwareResetCompletion(uint32_t aTimeoutMs);
 
     /**
+     * Initialize socket
+     *
+     * @retval TRUE  Socket initialization is successful.
+     * @retval FALSE Socket initialization is failed.
+     *
+     */
+    otError InitSocket();
+
+    /**
      * Reset socket interface to intitial state.
      *
      */
@@ -257,6 +266,7 @@
                                              ///< descriptor to become available.
         kMaxRetriesForSocketCloseCheck = 3,  ///< Maximum retry times for checking
                                              ///< if socket is closed.
+        kMaxRetriesForSocketInit = 3,        ///< Maximum retry times for socket initialization.
     };
 
     ReceiveFrameCallback mReceiveFrameCallback;
diff --git a/uwb/aidl/Android.bp b/uwb/aidl/Android.bp
index abd6a23..655d8a2 100755
--- a/uwb/aidl/Android.bp
+++ b/uwb/aidl/Android.bp
@@ -16,6 +16,7 @@
     srcs: ["android/hardware/uwb/*.aidl"],
     stability: "vintf",
     host_supported: true,
+    frozen: true,
     backend: {
         java: {
             sdk_version: "module_Tiramisu",
@@ -56,6 +57,7 @@
     vendor_available: true,
     srcs: ["android/hardware/uwb/fira_android/*.aidl"],
     stability: "vintf",
+    frozen: false,
     backend: {
         java: {
             sdk_version: "module_Tiramisu",
diff --git a/weaver/vts/VtsHalWeaverTargetTest.cpp b/weaver/vts/VtsHalWeaverTargetTest.cpp
index 8952dfc..faa8416 100644
--- a/weaver/vts/VtsHalWeaverTargetTest.cpp
+++ b/weaver/vts/VtsHalWeaverTargetTest.cpp
@@ -220,13 +220,10 @@
             used_slots.insert(slot);
         }
     }
-    // Starting in Android 14, the system will always use at least one Weaver slot if Weaver is
-    // supported at all.  This is true even if an LSKF hasn't been set yet, since Weaver is used to
-    // protect the initial binding of each user's synthetic password to ensure that binding can be
-    // securely deleted if an LSKF is set later.  Make sure we saw at least one slot, as otherwise
-    // the Weaver implementation must have a bug that makes it not fully usable by Android.
-    ASSERT_FALSE(used_slots.empty())
-            << "Could not determine which Weaver slots are in use by the system";
+
+    // We should assert !used_slots.empty() here, but that can't be done yet due to
+    // config_disableWeaverOnUnsecuredUsers being supported.  The value of that option is not
+    // accessible from here, so we have to assume it might be set to true.
 
     // Find the first free slot.
     int found = 0;
diff --git a/wifi/netlinkinterceptor/aidl/Android.bp b/wifi/netlinkinterceptor/aidl/Android.bp
index 8c04e31..bc02125 100644
--- a/wifi/netlinkinterceptor/aidl/Android.bp
+++ b/wifi/netlinkinterceptor/aidl/Android.bp
@@ -29,6 +29,7 @@
     vendor_available: true,
     srcs: ["android/hardware/net/nlinterceptor/*.aidl"],
     stability: "vintf",
+    frozen: true,
     backend: {
         java: {
             enabled: false,