Merge "GnssStatus for GnssMeasurement-only VTS test" into udc-dev
diff --git a/audio/aidl/android/hardware/audio/core/IModule.aidl b/audio/aidl/android/hardware/audio/core/IModule.aidl
index 7830501..7622d9a 100644
--- a/audio/aidl/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/android/hardware/audio/core/IModule.aidl
@@ -192,6 +192,19 @@
      * device address is specified for a point-to-multipoint external device
      * connection.
      *
+     * Since not all modules have a DSP that could perform sample rate and
+     * format conversions, behavior related to mix port configurations may vary.
+     * For modules with a DSP, mix ports can be pre-configured and have a fixed
+     * set of audio profiles supported by the DSP. For modules without a DSP,
+     * audio profiles of mix ports may change after connecting an external
+     * device. The typical case is that the mix port has an empty set of
+     * profiles when no external devices are connected, and after external
+     * device connection it receives the same set of profiles as the device
+     * ports that they can be routed to. The client will re-query current port
+     * configurations using 'getAudioPorts'. All mix ports that can be routed to
+     * the connected device port must have a non-empty set of audio profiles
+     * after successful connection of an external device.
+     *
      * Handling of a disconnect is done in a reverse order:
      *  1. Reset port configuration using the 'resetAudioPortConfig' method.
      *  2. Release the connected device port by calling the 'disconnectExternalDevice'
diff --git a/audio/aidl/common/Android.bp b/audio/aidl/common/Android.bp
index a3f7f0b..4c6a74e 100644
--- a/audio/aidl/common/Android.bp
+++ b/audio/aidl/common/Android.bp
@@ -41,6 +41,20 @@
     ],
 }
 
+cc_library {
+    name: "libaudioaidlranges",
+    host_supported: true,
+    vendor_available: true,
+    static_libs: [
+        "android.hardware.audio.effect-V1-ndk",
+    ],
+    export_include_dirs: ["include"],
+    header_libs: ["libaudioaidl_headers"],
+    srcs: [
+        "EffectRangeSpecific.cpp",
+    ],
+}
+
 cc_test {
     name: "libaudioaidlcommon_test",
     host_supported: true,
diff --git a/audio/aidl/common/EffectRangeSpecific.cpp b/audio/aidl/common/EffectRangeSpecific.cpp
new file mode 100644
index 0000000..bd78ea0
--- /dev/null
+++ b/audio/aidl/common/EffectRangeSpecific.cpp
@@ -0,0 +1,161 @@
+/*

+ * Copyright (C) 2023 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+#include <aidl/android/hardware/audio/effect/DynamicsProcessing.h>

+#include <aidl/android/hardware/audio/effect/Range.h>

+

+#include "EffectRangeSpecific.h"

+#include "effect-impl/EffectRange.h"

+

+namespace aidl::android::hardware::audio::effect {

+

+namespace DynamicsProcessingRanges {

+

+static bool isInputGainConfigInRange(const std::vector<DynamicsProcessing::InputGain>& cfgs,

+                                     const DynamicsProcessing::InputGain& min,

+                                     const DynamicsProcessing::InputGain& max) {

+    auto func = [](const DynamicsProcessing::InputGain& arg) {

+        return std::make_tuple(arg.channel, arg.gainDb);

+    };

+    return isTupleInRange(cfgs, min, max, func);

+}

+

+static bool isLimiterConfigInRange(const std::vector<DynamicsProcessing::LimiterConfig>& cfgs,

+                                   const DynamicsProcessing::LimiterConfig& min,

+                                   const DynamicsProcessing::LimiterConfig& max) {

+    auto func = [](const DynamicsProcessing::LimiterConfig& arg) {

+        return std::make_tuple(arg.channel, arg.enable, arg.linkGroup, arg.attackTimeMs,

+                               arg.releaseTimeMs, arg.ratio, arg.thresholdDb, arg.postGainDb);

+    };

+    return isTupleInRange(cfgs, min, max, func);

+}

+

+static bool isMbcBandConfigInRange(const std::vector<DynamicsProcessing::MbcBandConfig>& cfgs,

+                                   const DynamicsProcessing::MbcBandConfig& min,

+                                   const DynamicsProcessing::MbcBandConfig& max) {

+    auto func = [](const DynamicsProcessing::MbcBandConfig& arg) {

+        return std::make_tuple(arg.channel, arg.band, arg.enable, arg.cutoffFrequencyHz,

+                               arg.attackTimeMs, arg.releaseTimeMs, arg.ratio, arg.thresholdDb,

+                               arg.kneeWidthDb, arg.noiseGateThresholdDb, arg.expanderRatio,

+                               arg.preGainDb, arg.postGainDb);

+    };

+    return isTupleInRange(cfgs, min, max, func);

+}

+

+static bool isEqBandConfigInRange(const std::vector<DynamicsProcessing::EqBandConfig>& cfgs,

+                                  const DynamicsProcessing::EqBandConfig& min,

+                                  const DynamicsProcessing::EqBandConfig& max) {

+    auto func = [](const DynamicsProcessing::EqBandConfig& arg) {

+        return std::make_tuple(arg.channel, arg.band, arg.enable, arg.cutoffFrequencyHz,

+                               arg.gainDb);

+    };

+    return isTupleInRange(cfgs, min, max, func);

+}

+

+static bool isChannelConfigInRange(const std::vector<DynamicsProcessing::ChannelConfig>& cfgs,

+                                   const DynamicsProcessing::ChannelConfig& min,

+                                   const DynamicsProcessing::ChannelConfig& max) {

+    auto func = [](const DynamicsProcessing::ChannelConfig& arg) {

+        return std::make_tuple(arg.channel, arg.enable);

+    };

+    return isTupleInRange(cfgs, min, max, func);

+}

+

+static bool isEngineConfigInRange(const DynamicsProcessing::EngineArchitecture& cfg,

+                                  const DynamicsProcessing::EngineArchitecture& min,

+                                  const DynamicsProcessing::EngineArchitecture& max) {

+    auto func = [](const DynamicsProcessing::EngineArchitecture& arg) {

+        return std::make_tuple(arg.resolutionPreference, arg.preferredProcessingDurationMs,

+                               arg.preEqStage.inUse, arg.preEqStage.bandCount,

+                               arg.postEqStage.inUse, arg.postEqStage.bandCount, arg.mbcStage.inUse,

+                               arg.mbcStage.bandCount, arg.limiterInUse);

+    };

+    return isTupleInRange(func(cfg), func(min), func(max));

+}

+

+static int locateMinMaxForTag(DynamicsProcessing::Tag tag,

+                              const std::vector<Range::DynamicsProcessingRange>& ranges) {

+    for (int i = 0; i < (int)ranges.size(); i++) {

+        if (tag == ranges[i].min.getTag() && tag == ranges[i].max.getTag()) {

+            return i;

+        }

+    }

+    return -1;

+}

+

+bool isParamInRange(const DynamicsProcessing& dp,

+                    const std::vector<Range::DynamicsProcessingRange>& ranges) {

+    auto tag = dp.getTag();

+    int i = locateMinMaxForTag(tag, ranges);

+    if (i == -1) return true;

+

+    switch (tag) {

+        case DynamicsProcessing::engineArchitecture: {

+            return isEngineConfigInRange(

+                    dp.get<DynamicsProcessing::engineArchitecture>(),

+                    ranges[i].min.get<DynamicsProcessing::engineArchitecture>(),

+                    ranges[i].max.get<DynamicsProcessing::engineArchitecture>());

+        }

+        case DynamicsProcessing::preEq: {

+            return isChannelConfigInRange(dp.get<DynamicsProcessing::preEq>(),

+                                          ranges[i].min.get<DynamicsProcessing::preEq>()[0],

+                                          ranges[i].max.get<DynamicsProcessing::preEq>()[0]);

+        }

+        case DynamicsProcessing::postEq: {

+            return isChannelConfigInRange(dp.get<DynamicsProcessing::postEq>(),

+                                          ranges[i].min.get<DynamicsProcessing::postEq>()[0],

+                                          ranges[i].max.get<DynamicsProcessing::postEq>()[0]);

+        }

+        case DynamicsProcessing::mbc: {

+            return isChannelConfigInRange(dp.get<DynamicsProcessing::mbc>(),

+                                          ranges[i].min.get<DynamicsProcessing::mbc>()[0],

+                                          ranges[i].max.get<DynamicsProcessing::mbc>()[0]);

+        }

+        case DynamicsProcessing::preEqBand: {

+            return isEqBandConfigInRange(dp.get<DynamicsProcessing::preEqBand>(),

+                                         ranges[i].min.get<DynamicsProcessing::preEqBand>()[0],

+                                         ranges[i].max.get<DynamicsProcessing::preEqBand>()[0]);

+        }

+        case DynamicsProcessing::postEqBand: {

+            return isEqBandConfigInRange(dp.get<DynamicsProcessing::postEqBand>(),

+                                         ranges[i].min.get<DynamicsProcessing::postEqBand>()[0],

+                                         ranges[i].max.get<DynamicsProcessing::postEqBand>()[0]);

+        }

+        case DynamicsProcessing::mbcBand: {

+            return isMbcBandConfigInRange(dp.get<DynamicsProcessing::mbcBand>(),

+                                          ranges[i].min.get<DynamicsProcessing::mbcBand>()[0],

+                                          ranges[i].max.get<DynamicsProcessing::mbcBand>()[0]);

+        }

+        case DynamicsProcessing::limiter: {

+            return isLimiterConfigInRange(dp.get<DynamicsProcessing::limiter>(),

+                                          ranges[i].min.get<DynamicsProcessing::limiter>()[0],

+                                          ranges[i].max.get<DynamicsProcessing::limiter>()[0]);

+        }

+        case DynamicsProcessing::inputGain: {

+            return isInputGainConfigInRange(dp.get<DynamicsProcessing::inputGain>(),

+                                            ranges[i].min.get<DynamicsProcessing::inputGain>()[0],

+                                            ranges[i].max.get<DynamicsProcessing::inputGain>()[0]);

+        }

+        default: {

+            return true;

+        }

+    }

+    return true;

+}

+

+}  // namespace DynamicsProcessingRanges

+

+}  // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
diff --git a/audio/aidl/common/include/EffectRangeSpecific.h b/audio/aidl/common/include/EffectRangeSpecific.h
new file mode 100644
index 0000000..c7262bb
--- /dev/null
+++ b/audio/aidl/common/include/EffectRangeSpecific.h
@@ -0,0 +1,28 @@
+/*

+ * Copyright (C) 2023 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+#pragma once

+

+namespace aidl::android::hardware::audio::effect {

+

+namespace DynamicsProcessingRanges {

+

+bool isParamInRange(const DynamicsProcessing& dp,

+                    const std::vector<Range::DynamicsProcessingRange>& ranges);

+

+}  // namespace DynamicsProcessingRanges

+

+}  // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
diff --git a/audio/aidl/common/include/Utils.h b/audio/aidl/common/include/Utils.h
index 2cf862c..305c924 100644
--- a/audio/aidl/common/include/Utils.h
+++ b/audio/aidl/common/include/Utils.h
@@ -99,6 +99,12 @@
     return 0;
 }
 
+constexpr bool isDefaultAudioFormat(
+        const ::aidl::android::media::audio::common::AudioFormatDescription& desc) {
+    return desc.type == ::aidl::android::media::audio::common::AudioFormatType::DEFAULT &&
+           desc.pcm == ::aidl::android::media::audio::common::PcmType::DEFAULT;
+}
+
 constexpr bool isTelephonyDeviceType(
         ::aidl::android::media::audio::common::AudioDeviceType device) {
     return device == ::aidl::android::media::audio::common::AudioDeviceType::IN_TELEPHONY_RX ||
diff --git a/audio/aidl/default/EffectFactory.cpp b/audio/aidl/default/EffectFactory.cpp
index c1ac3f2..f0687cc 100644
--- a/audio/aidl/default/EffectFactory.cpp
+++ b/audio/aidl/default/EffectFactory.cpp
@@ -75,6 +75,8 @@
             RETURN_IF(!libInterface || !libInterface->queryEffectFunc, EX_NULL_POINTER,
                       "dlNullQueryEffectFunc");
             RETURN_IF_BINDER_EXCEPTION(libInterface->queryEffectFunc(&id.uuid, &desc));
+            // update proxy UUID with information from config xml
+            desc.common.id.proxy = id.proxy;
             _aidl_return->emplace_back(std::move(desc));
         }
     }
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 984b9a1..6b417a4 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -143,6 +143,21 @@
     }
 }
 
+std::ostream& operator<<(std::ostream& os, Module::Type t) {
+    switch (t) {
+        case Module::Type::DEFAULT:
+            os << "default";
+            break;
+        case Module::Type::R_SUBMIX:
+            os << "r_submix";
+            break;
+        case Module::Type::USB:
+            os << "usb";
+            break;
+    }
+    return os;
+}
+
 void Module::cleanUpPatch(int32_t patchId) {
     erase_all_values(mPatches, std::set<int32_t>{patchId});
 }
@@ -352,16 +367,17 @@
 
 ndk::ScopedAStatus Module::setModuleDebug(
         const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) {
-    LOG(DEBUG) << __func__ << ": old flags:" << mDebug.toString()
+    LOG(DEBUG) << __func__ << ": " << mType << ": old flags:" << mDebug.toString()
                << ", new flags: " << in_debug.toString();
     if (mDebug.simulateDeviceConnections != in_debug.simulateDeviceConnections &&
         !mConnectedDevicePorts.empty()) {
-        LOG(ERROR) << __func__ << ": attempting to change device connections simulation "
-                   << "while having external devices connected";
+        LOG(ERROR) << __func__ << ": " << mType
+                   << ": attempting to change device connections simulation while having external "
+                   << "devices connected";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
     if (in_debug.streamTransientStateDelayMs < 0) {
-        LOG(ERROR) << __func__ << ": streamTransientStateDelayMs is negative: "
+        LOG(ERROR) << __func__ << ": " << mType << ": streamTransientStateDelayMs is negative: "
                    << in_debug.streamTransientStateDelayMs;
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
@@ -440,38 +456,45 @@
         LOG(DEBUG) << __func__ << ": device port " << connectedPort.id << " device set to "
                    << connectedDevicePort.device.toString();
         // Check if there is already a connected port with for the same external device.
-        for (auto connectedPortId : mConnectedDevicePorts) {
-            auto connectedPortIt = findById<AudioPort>(ports, connectedPortId);
+        for (auto connectedPortPair : mConnectedDevicePorts) {
+            auto connectedPortIt = findById<AudioPort>(ports, connectedPortPair.first);
             if (connectedPortIt->ext.get<AudioPortExt::Tag::device>().device ==
                 connectedDevicePort.device) {
                 LOG(ERROR) << __func__ << ": device " << connectedDevicePort.device.toString()
-                           << " is already connected at the device port id " << connectedPortId;
+                           << " is already connected at the device port id "
+                           << connectedPortPair.first;
                 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
             }
         }
     }
 
     if (!mDebug.simulateDeviceConnections) {
-        // In a real HAL here we would attempt querying the profiles from the device.
-        LOG(ERROR) << __func__ << ": failed to query supported device profiles";
-        // TODO: Check the return value when it is ready for actual devices.
-        populateConnectedDevicePort(&connectedPort);
+        if (ndk::ScopedAStatus status = populateConnectedDevicePort(&connectedPort);
+            !status.isOk()) {
+            return status;
+        }
+    } else {
+        auto& connectedProfiles = getConfig().connectedProfiles;
+        if (auto connectedProfilesIt = connectedProfiles.find(templateId);
+            connectedProfilesIt != connectedProfiles.end()) {
+            connectedPort.profiles = connectedProfilesIt->second;
+        }
+    }
+    if (connectedPort.profiles.empty()) {
+        LOG(ERROR) << "Profiles of a connected port still empty after connecting external device "
+                   << connectedPort.toString();
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
 
     connectedPort.id = ++getConfig().nextPortId;
-    mConnectedDevicePorts.insert(connectedPort.id);
+    auto [connectedPortsIt, _] =
+            mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::vector<int32_t>()));
     LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, "
                << "connected port ID " << connectedPort.id;
-    auto& connectedProfiles = getConfig().connectedProfiles;
-    if (auto connectedProfilesIt = connectedProfiles.find(templateId);
-        connectedProfilesIt != connectedProfiles.end()) {
-        connectedPort.profiles = connectedProfilesIt->second;
-    }
     ports.push_back(connectedPort);
     onExternalDeviceConnectionChanged(connectedPort, true /*connected*/);
-    *_aidl_return = std::move(connectedPort);
 
+    std::vector<int32_t> routablePortIds;
     std::vector<AudioRoute> newRoutes;
     auto& routes = getConfig().routes;
     for (auto& r : routes) {
@@ -481,15 +504,30 @@
             newRoute.sinkPortId = connectedPort.id;
             newRoute.isExclusive = r.isExclusive;
             newRoutes.push_back(std::move(newRoute));
+            routablePortIds.insert(routablePortIds.end(), r.sourcePortIds.begin(),
+                                   r.sourcePortIds.end());
         } else {
             auto& srcs = r.sourcePortIds;
             if (std::find(srcs.begin(), srcs.end(), templateId) != srcs.end()) {
                 srcs.push_back(connectedPort.id);
+                routablePortIds.push_back(r.sinkPortId);
             }
         }
     }
     routes.insert(routes.end(), newRoutes.begin(), newRoutes.end());
 
+    // Note: this is a simplistic approach assuming that a mix port can only be populated
+    // from a single device port. Implementing support for stuffing dynamic profiles with a superset
+    // of all profiles from all routable dynamic device ports would be more involved.
+    for (const auto mixPortId : routablePortIds) {
+        auto portsIt = findById<AudioPort>(ports, mixPortId);
+        if (portsIt != ports.end() && portsIt->profiles.empty()) {
+            portsIt->profiles = connectedPort.profiles;
+            connectedPortsIt->second.push_back(portsIt->id);
+        }
+    }
+    *_aidl_return = std::move(connectedPort);
+
     return ndk::ScopedAStatus::ok();
 }
 
@@ -504,7 +542,8 @@
         LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a device port";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
-    if (mConnectedDevicePorts.count(in_portId) == 0) {
+    auto connectedPortsIt = mConnectedDevicePorts.find(in_portId);
+    if (connectedPortsIt == mConnectedDevicePorts.end()) {
         LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a connected device port";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
@@ -525,7 +564,6 @@
     }
     onExternalDeviceConnectionChanged(*portIt, false /*connected*/);
     ports.erase(portIt);
-    mConnectedDevicePorts.erase(in_portId);
     LOG(DEBUG) << __func__ << ": connected device port " << in_portId << " released";
 
     auto& routes = getConfig().routes;
@@ -540,6 +578,14 @@
         }
     }
 
+    for (const auto mixPortId : connectedPortsIt->second) {
+        auto mixPortIt = findById<AudioPort>(ports, mixPortId);
+        if (mixPortIt != ports.end()) {
+            mixPortIt->profiles = {};
+        }
+    }
+    mConnectedDevicePorts.erase(connectedPortsIt);
+
     return ndk::ScopedAStatus::ok();
 }
 
diff --git a/audio/aidl/default/include/core-impl/Configuration.h b/audio/aidl/default/include/core-impl/Configuration.h
index 4dd0133..70320e4 100644
--- a/audio/aidl/default/include/core-impl/Configuration.h
+++ b/audio/aidl/default/include/core-impl/Configuration.h
@@ -33,7 +33,8 @@
     std::vector<::aidl::android::media::audio::common::AudioPort> ports;
     std::vector<::aidl::android::media::audio::common::AudioPortConfig> portConfigs;
     std::vector<::aidl::android::media::audio::common::AudioPortConfig> initialConfigs;
-    // Port id -> List of profiles to use when the device port state is set to 'connected'.
+    // Port id -> List of profiles to use when the device port state is set to 'connected'
+    // in connection simulation mode.
     std::map<int32_t, std::vector<::aidl::android::media::audio::common::AudioProfile>>
             connectedProfiles;
     std::vector<AudioRoute> routes;
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 2cbda7d..83ecfaa 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -177,8 +177,10 @@
     ChildInterface<IBluetooth> mBluetooth;
     ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
     ChildInterface<IBluetoothLe> mBluetoothLe;
-    // ids of ports created at runtime via 'connectExternalDevice'.
-    std::set<int32_t> mConnectedDevicePorts;
+    // ids of device ports created at runtime via 'connectExternalDevice'.
+    // Also stores ids of mix ports with dynamic profiles which got populated from the connected
+    // port.
+    std::map<int32_t, std::vector<int32_t>> mConnectedDevicePorts;
     Streams mStreams;
     // Maps port ids and port config ids to patch ids.
     // Multimap because both ports and configs can be used by multiple patches.
diff --git a/audio/aidl/default/include/core-impl/StreamUsb.h b/audio/aidl/default/include/core-impl/StreamUsb.h
index f1815dd..36e64cb 100644
--- a/audio/aidl/default/include/core-impl/StreamUsb.h
+++ b/audio/aidl/default/include/core-impl/StreamUsb.h
@@ -56,7 +56,7 @@
     std::vector<::aidl::android::media::audio::common::AudioDeviceAddress> mConnectedDevices
             GUARDED_BY(mLock);
     std::vector<std::shared_ptr<alsa_device_proxy>> mAlsaDeviceProxies GUARDED_BY(mLock);
-    bool mIsStandby = false;
+    bool mIsStandby = true;
 };
 
 class StreamInUsb final : public StreamIn {
diff --git a/audio/aidl/default/include/effect-impl/EffectRange.h b/audio/aidl/default/include/effect-impl/EffectRange.h
new file mode 100644
index 0000000..a3ea01f
--- /dev/null
+++ b/audio/aidl/default/include/effect-impl/EffectRange.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace aidl::android::hardware::audio::effect {
+
+template <typename T>
+bool isInRange(const T& value, const T& low, const T& high) {
+    return (value >= low) && (value <= high);
+}
+
+template <typename T, std::size_t... Is>
+bool isTupleInRange(const T& test, const T& min, const T& max, std::index_sequence<Is...>) {
+    return (isInRange(std::get<Is>(test), std::get<Is>(min), std::get<Is>(max)) && ...);
+}
+
+template <typename T, std::size_t TupSize = std::tuple_size_v<T>>
+bool isTupleInRange(const T& test, const T& min, const T& max) {
+    return isTupleInRange(test, min, max, std::make_index_sequence<TupSize>{});
+}
+
+template <typename T, typename F>
+bool isTupleInRange(const std::vector<T>& cfgs, const T& min, const T& max, const F& func) {
+    auto minT = func(min), maxT = func(max);
+    return std::all_of(cfgs.cbegin(), cfgs.cend(),
+                       [&](const T& cfg) { return isTupleInRange(func(cfg), minT, maxT); });
+}
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/usb/ModuleUsb.cpp b/audio/aidl/default/usb/ModuleUsb.cpp
index 80b0a5b..89895bf 100644
--- a/audio/aidl/default/usb/ModuleUsb.cpp
+++ b/audio/aidl/default/usb/ModuleUsb.cpp
@@ -120,7 +120,11 @@
     const bool isInput = isUsbInputDeviceType(devicePort.device.type.type);
     alsa_device_profile profile;
     profile_init(&profile, isInput ? PCM_IN : PCM_OUT);
+    profile.card = alsaAddress[0];
+    profile.device = alsaAddress[1];
     if (!profile_read_device_info(&profile)) {
+        LOG(ERROR) << __func__ << ": failed to read device info, card=" << profile.card
+                   << ", device=" << profile.device;
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
 
diff --git a/audio/aidl/default/usb/StreamUsb.cpp b/audio/aidl/default/usb/StreamUsb.cpp
index fbfe0f1..5d1d7fe 100644
--- a/audio/aidl/default/usb/StreamUsb.cpp
+++ b/audio/aidl/default/usb/StreamUsb.cpp
@@ -107,10 +107,13 @@
 ::android::status_t DriverUsb::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                         int32_t* latencyMs) {
     if (!mConfig.has_value() || mConnectedDevices.empty()) {
+        LOG(ERROR) << __func__ << ": failed, has config: " << mConfig.has_value()
+                   << ", has connected devices: " << mConnectedDevices.empty();
         return ::android::NO_INIT;
     }
     if (mIsStandby) {
         if (::android::status_t status = exitStandby(); status != ::android::OK) {
+            LOG(ERROR) << __func__ << ": failed to exit standby, status=" << status;
             return status;
         }
     }
@@ -151,6 +154,7 @@
     std::vector<std::shared_ptr<alsa_device_proxy>> alsaDeviceProxies;
     for (const auto& device : connectedDevices) {
         alsa_device_profile profile;
+        profile_init(&profile, mIsInput ? PCM_IN : PCM_OUT);
         profile.card = device.get<AudioDeviceAddress::alsa>()[0];
         profile.device = device.get<AudioDeviceAddress::alsa>()[1];
         if (!profile_read_device_info(&profile)) {
@@ -174,6 +178,11 @@
                        << " error=" << err;
             return ::android::UNKNOWN_ERROR;
         }
+        if (int err = proxy_open(proxy.get()); err != 0) {
+            LOG(ERROR) << __func__ << ": failed to open device, address=" << device.toString()
+                       << " error=" << err;
+            return ::android::UNKNOWN_ERROR;
+        }
         alsaDeviceProxies.push_back(std::move(proxy));
     }
     {
diff --git a/audio/aidl/default/usb/UsbAlsaUtils.cpp b/audio/aidl/default/usb/UsbAlsaUtils.cpp
index 3a74c2a..74d9c28 100644
--- a/audio/aidl/default/usb/UsbAlsaUtils.cpp
+++ b/audio/aidl/default/usb/UsbAlsaUtils.cpp
@@ -114,8 +114,8 @@
     static const AudioFormatDescToPcmFormatMap formatDescToPcmFormatMap = {
             {make_AudioFormatDescription(PcmType::UINT_8_BIT), PCM_FORMAT_S8},
             {make_AudioFormatDescription(PcmType::INT_16_BIT), PCM_FORMAT_S16_LE},
-            {make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_LE},
-            {make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_3LE},
+            {make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_LE},
+            {make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_3LE},
             {make_AudioFormatDescription(PcmType::INT_32_BIT), PCM_FORMAT_S32_LE},
             {make_AudioFormatDescription(PcmType::FLOAT_32_BIT), PCM_FORMAT_FLOAT_LE},
     };
diff --git a/audio/aidl/sounddose/vts/VtsHalSoundDoseFactoryTargetTest.cpp b/audio/aidl/sounddose/vts/VtsHalSoundDoseFactoryTargetTest.cpp
index df35bae..7722ccf 100644
--- a/audio/aidl/sounddose/vts/VtsHalSoundDoseFactoryTargetTest.cpp
+++ b/audio/aidl/sounddose/vts/VtsHalSoundDoseFactoryTargetTest.cpp
@@ -71,6 +71,7 @@
     std::shared_ptr<ISoundDoseFactory> soundDoseFactory;
 };
 
+// @VsrTest = VSR-5.5-002.001
 TEST_P(SoundDoseFactory, GetSoundDoseForSameModule) {
     const std::string module = "primary";
 
diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp
index 18aa6f0..852255d 100644
--- a/audio/aidl/vts/Android.bp
+++ b/audio/aidl/vts/Android.bp
@@ -84,6 +84,7 @@
 cc_test {
     name: "VtsHalDynamicsProcessingTargetTest",
     defaults: ["VtsHalAudioTargetTestDefaults"],
+    static_libs: ["libaudioaidlranges"],
     srcs: ["VtsHalDynamicsProcessingTest.cpp"],
 }
 
diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h
index f6683cc..831977b 100644
--- a/audio/aidl/vts/EffectHelper.h
+++ b/audio/aidl/vts/EffectHelper.h
@@ -228,10 +228,10 @@
      */
     template <typename S, typename = std::enable_if_t<std::is_arithmetic_v<S>>>
     static std::set<S> expandTestValueBasic(std::set<S>& s) {
-        const auto min = *s.begin(), max = *s.rbegin();
         const auto minLimit = std::numeric_limits<S>::min(),
                    maxLimit = std::numeric_limits<S>::max();
         if (s.size()) {
+            const auto min = *s.begin(), max = *s.rbegin();
             s.insert(min + (max - min) / 2);
             if (min != minLimit) {
                 s.insert(min - 1);
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index e790d4f..825b865 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -129,7 +129,9 @@
     using Tag = AudioDeviceAddress::Tag;
     if (std::string_view connection = description.connection;
         connection == AudioDeviceDescription::CONNECTION_BT_A2DP ||
-        connection == AudioDeviceDescription::CONNECTION_BT_LE ||
+        // Note: BT LE Broadcast uses a "group id".
+        (description.type != AudioDeviceType::OUT_BROADCAST &&
+         connection == AudioDeviceDescription::CONNECTION_BT_LE) ||
         connection == AudioDeviceDescription::CONNECTION_BT_SCO ||
         connection == AudioDeviceDescription::CONNECTION_WIRELESS) {
         return Tag::mac;
@@ -1778,6 +1780,42 @@
     }
 }
 
+// Note: This test relies on simulation of external device connections by the HAL module.
+TEST_P(AudioCoreModule, ExternalDeviceMixPortConfigs) {
+    // After an external device has been connected, all mix ports that can be routed
+    // to the device port for the connected device must have non-empty profiles.
+    ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+    std::vector<AudioPort> externalDevicePorts = moduleConfig->getExternalDevicePorts();
+    if (externalDevicePorts.empty()) {
+        GTEST_SKIP() << "No external devices in the module.";
+    }
+    for (const auto& port : externalDevicePorts) {
+        WithDevicePortConnectedState portConnected(GenerateUniqueDeviceAddress(port));
+        ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
+        std::vector<AudioRoute> routes;
+        ASSERT_IS_OK(module->getAudioRoutesForAudioPort(portConnected.getId(), &routes));
+        std::vector<AudioPort> allPorts;
+        ASSERT_IS_OK(module->getAudioPorts(&allPorts));
+        for (const auto& r : routes) {
+            if (r.sinkPortId == portConnected.getId()) {
+                for (const auto& srcPortId : r.sourcePortIds) {
+                    const auto srcPortIt = findById(allPorts, srcPortId);
+                    ASSERT_NE(allPorts.end(), srcPortIt) << "port ID " << srcPortId;
+                    EXPECT_NE(0UL, srcPortIt->profiles.size())
+                            << " source port " << srcPortIt->toString() << " must have its profiles"
+                            << " populated following external device connection";
+                }
+            } else {
+                const auto sinkPortIt = findById(allPorts, r.sinkPortId);
+                ASSERT_NE(allPorts.end(), sinkPortIt) << "port ID " << r.sinkPortId;
+                EXPECT_NE(0UL, sinkPortIt->profiles.size())
+                        << " source port " << sinkPortIt->toString() << " must have its"
+                        << " profiles populated following external device connection";
+            }
+        }
+    }
+}
+
 TEST_P(AudioCoreModule, MasterMute) {
     bool isSupported = false;
     EXPECT_NO_FATAL_FAILURE(TestAccessors<bool>(module.get(), &IModule::getMasterMute,
@@ -3532,6 +3570,7 @@
     return ndk::ScopedAStatus::ok();
 }
 
+// @VsrTest = VSR-5.5-002.001
 TEST_P(AudioCoreSoundDose, SameInstance) {
     if (soundDose == nullptr) {
         GTEST_SKIP() << "SoundDose is not supported";
@@ -3543,6 +3582,7 @@
             << "getSoundDose must return the same interface instance across invocations";
 }
 
+// @VsrTest = VSR-5.5-002.001
 TEST_P(AudioCoreSoundDose, GetSetOutputRs2UpperBound) {
     if (soundDose == nullptr) {
         GTEST_SKIP() << "SoundDose is not supported";
@@ -3557,6 +3597,7 @@
     EXPECT_TRUE(isSupported) << "Getting/Setting RS2 upper bound must be supported";
 }
 
+// @VsrTest = VSR-5.5-002.001
 TEST_P(AudioCoreSoundDose, CheckDefaultRs2UpperBound) {
     if (soundDose == nullptr) {
         GTEST_SKIP() << "SoundDose is not supported";
@@ -3567,6 +3608,7 @@
     EXPECT_EQ(rs2Value, ISoundDose::DEFAULT_MAX_RS2);
 }
 
+// @VsrTest = VSR-5.5-002.001
 TEST_P(AudioCoreSoundDose, RegisterSoundDoseCallbackTwiceThrowsException) {
     if (soundDose == nullptr) {
         GTEST_SKIP() << "SoundDose is not supported";
@@ -3577,6 +3619,7 @@
             << "Registering sound dose callback twice should throw EX_ILLEGAL_STATE";
 }
 
+// @VsrTest = VSR-5.5-002.001
 TEST_P(AudioCoreSoundDose, RegisterSoundDoseNullCallbackThrowsException) {
     if (soundDose == nullptr) {
         GTEST_SKIP() << "SoundDose is not supported";
diff --git a/audio/aidl/vts/VtsHalAudioCoreTargetTest.xml b/audio/aidl/vts/VtsHalAudioCoreTargetTest.xml
new file mode 100644
index 0000000..dfc1039
--- /dev/null
+++ b/audio/aidl/vts/VtsHalAudioCoreTargetTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs VtsHalAudioCoreTargetTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.StopServicesSetup"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="setprop vts.native_server.on 1"/>
+        <option name="teardown-command" value="setprop vts.native_server.on 0"/>
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalAudioCoreTargetTest" />
+        <option name="native-test-timeout" value="10m" />
+    </test>
+</configuration>
diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
index df66bd3..d8ad6c9 100644
--- a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
@@ -139,7 +139,10 @@
     Descriptor desc;
     ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
     ASSERT_NO_FATAL_FAILURE(getDescriptor(mEffect, desc));
-    EXPECT_EQ(mDescriptor.common, desc.common);
+    EXPECT_EQ(mDescriptor.common.id.type, desc.common.id.type);
+    EXPECT_EQ(mDescriptor.common.id.uuid, desc.common.id.uuid);
+    EXPECT_EQ(mDescriptor.common.name, desc.common.name);
+    EXPECT_EQ(mDescriptor.common.implementor, desc.common.implementor);
     // Effect implementation Must fill in implementor and name
     EXPECT_NE("", desc.common.name);
     EXPECT_NE("", desc.common.implementor);
@@ -176,7 +179,11 @@
 
     ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
     ASSERT_NO_FATAL_FAILURE(getDescriptor(mEffect, desc));
-    EXPECT_EQ(1ul, idSet.count(desc.common.id));
+    int uuidCount = std::count_if(idSet.begin(), idSet.end(), [&](const auto& id) {
+        return id.uuid == desc.common.id.uuid && id.type == desc.common.id.type;
+    });
+
+    EXPECT_EQ(1, uuidCount);
     ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
 }
 
diff --git a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
index c62a24e..033e3b5 100644
--- a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
+++ b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
@@ -25,8 +25,10 @@
 #include <Utils.h>
 
 #include "EffectHelper.h"
+#include "EffectRangeSpecific.h"
 
 using namespace android;
+using namespace aidl::android::hardware::audio::effect::DynamicsProcessingRanges;
 
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::DynamicsProcessing;
@@ -95,6 +97,19 @@
     template <typename T>
     bool isAidlVectorEqual(const std::vector<T>& source, const std::vector<T>& target);
 
+    template <typename T>
+    bool isChannelConfigValid(const std::vector<T>& cfgs) {
+        auto& channelCount = mChannelCount;
+        return std::all_of(cfgs.cbegin(), cfgs.cend(), [channelCount](const T& cfg) {
+            return (cfg.channel >= 0 && cfg.channel < channelCount);
+        });
+    }
+
+    template <typename T>
+    bool isBandConfigValid(const std::vector<T>& cfgs, int bandCount);
+
+    bool isParamValid(const DynamicsProcessing::Tag& tag, const DynamicsProcessing& dp);
+
     // get set params and validate
     void SetAndGetDynamicsProcessingParameters();
 
@@ -133,9 +148,11 @@
     static const std::set<DynamicsProcessing::StageEnablement> kStageEnablementTestSet;
     static const std::set<std::vector<DynamicsProcessing::InputGain>> kInputGainTestSet;
 
+  protected:
+    int mChannelCount;
+
   private:
     int32_t mChannelLayout;
-    int mChannelCount;
     std::vector<std::pair<DynamicsProcessing::Tag, DynamicsProcessing>> mTags;
     void CleanUp() {
         mTags.clear();
@@ -152,6 +169,8 @@
                 {.inUse = true, .bandCount = DynamicsProcessingTestHelper::kBandCount},
                 {.inUse = true, .bandCount = 0},
                 {.inUse = true, .bandCount = -1},
+                {.inUse = false, .bandCount = 0},
+                {.inUse = false, .bandCount = -1},
                 {.inUse = false, .bandCount = DynamicsProcessingTestHelper::kBandCount}};
 
 // test value set for DynamicsProcessing::ChannelConfig
@@ -161,9 +180,7 @@
                  {.channel = 0, .enable = true},
                  {.channel = 1, .enable = false},
                  {.channel = 2, .enable = true}},
-
                 {{.channel = -1, .enable = false}, {.channel = 2, .enable = true}},
-
                 {{.channel = 0, .enable = true}, {.channel = 1, .enable = true}}};
 
 // test value set for DynamicsProcessing::InputGain
@@ -172,10 +189,65 @@
                 {{.channel = 0, .gainDb = 10.f},
                  {.channel = 1, .gainDb = 0.f},
                  {.channel = 2, .gainDb = -10.f}},
-
                 {{.channel = -1, .gainDb = -10.f}, {.channel = -2, .gainDb = 10.f}},
+                {{.channel = -1, .gainDb = 10.f}, {.channel = 0, .gainDb = -10.f}},
+                {{.channel = 0, .gainDb = 10.f}, {.channel = 1, .gainDb = -10.f}}};
 
-                {{.channel = -1, .gainDb = 10.f}, {.channel = 0, .gainDb = -10.f}}};
+template <typename T>
+bool DynamicsProcessingTestHelper::isBandConfigValid(const std::vector<T>& cfgs, int bandCount) {
+    std::vector<float> freqs(cfgs.size(), -1);
+    for (auto cfg : cfgs) {
+        if (cfg.channel < 0 || cfg.channel >= mChannelCount) return false;
+        if (cfg.band < 0 || cfg.band >= bandCount) return false;
+        freqs[cfg.band] = cfg.cutoffFrequencyHz;
+    }
+    if (std::count(freqs.begin(), freqs.end(), -1)) return false;
+    return std::is_sorted(freqs.begin(), freqs.end());
+}
+
+bool DynamicsProcessingTestHelper::isParamValid(const DynamicsProcessing::Tag& tag,
+                                                const DynamicsProcessing& dp) {
+    switch (tag) {
+        case DynamicsProcessing::preEq: {
+            if (!mEngineConfigApplied.preEqStage.inUse) return false;
+            return isChannelConfigValid(dp.get<DynamicsProcessing::preEq>());
+        }
+        case DynamicsProcessing::postEq: {
+            if (!mEngineConfigApplied.postEqStage.inUse) return false;
+            return isChannelConfigValid(dp.get<DynamicsProcessing::postEq>());
+        }
+        case DynamicsProcessing::mbc: {
+            if (!mEngineConfigApplied.mbcStage.inUse) return false;
+            return isChannelConfigValid(dp.get<DynamicsProcessing::mbc>());
+        }
+        case DynamicsProcessing::preEqBand: {
+            if (!mEngineConfigApplied.preEqStage.inUse) return false;
+            return isBandConfigValid(dp.get<DynamicsProcessing::preEqBand>(),
+                                     mEngineConfigApplied.preEqStage.bandCount);
+        }
+        case DynamicsProcessing::postEqBand: {
+            if (!mEngineConfigApplied.postEqStage.inUse) return false;
+            return isBandConfigValid(dp.get<DynamicsProcessing::postEqBand>(),
+                                     mEngineConfigApplied.postEqStage.bandCount);
+        }
+        case DynamicsProcessing::mbcBand: {
+            if (!mEngineConfigApplied.mbcStage.inUse) return false;
+            return isBandConfigValid(dp.get<DynamicsProcessing::mbcBand>(),
+                                     mEngineConfigApplied.mbcStage.bandCount);
+        }
+        case DynamicsProcessing::limiter: {
+            if (!mEngineConfigApplied.limiterInUse) return false;
+            return isChannelConfigValid(dp.get<DynamicsProcessing::limiter>());
+        }
+        case DynamicsProcessing::inputGain: {
+            return isChannelConfigValid(dp.get<DynamicsProcessing::inputGain>());
+        }
+        default: {
+            return true;
+        }
+    }
+    return true;
+}
 
 bool DynamicsProcessingTestHelper::isParamEqual(const DynamicsProcessing::Tag& tag,
                                                 const DynamicsProcessing& dpRef,
@@ -270,8 +342,8 @@
         // validate parameter
         Descriptor desc;
         ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
-        const bool valid =
-                isParameterValid<DynamicsProcessing, Range::dynamicsProcessing>(dp, desc);
+        bool valid = isParamInRange(dp, desc.capability.range.get<Range::dynamicsProcessing>());
+        if (valid) valid = isParamValid(tag, dp);
         const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
 
         // set parameter
@@ -429,10 +501,11 @@
         ::testing::Combine(
                 testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
                         IFactory::descriptor, getEffectTypeUuidDynamicsProcessing())),
-                testing::Values(DynamicsProcessing::ResolutionPreference::FAVOR_TIME_RESOLUTION,
-                                DynamicsProcessing::ResolutionPreference::
-                                        FAVOR_FREQUENCY_RESOLUTION),  // variant
-                testing::Values(-10.f, 0.f, 10.f),                    // processing duration
+                testing::Values(
+                        DynamicsProcessing::ResolutionPreference::FAVOR_TIME_RESOLUTION,
+                        DynamicsProcessing::ResolutionPreference::FAVOR_FREQUENCY_RESOLUTION,
+                        static_cast<DynamicsProcessing::ResolutionPreference>(-1)),  // variant
+                testing::Values(-10.f, 0.f, 10.f),  // processing duration
                 testing::ValuesIn(
                         DynamicsProcessingTestHelper::kStageEnablementTestSet),  // preEQ/postEQ/mbc
                 testing::Bool()),                                                // limiter enable
@@ -515,12 +588,12 @@
     LIMITER_MAX_NUM,
 };
 using LimiterConfigTestAdditional = std::array<float, LIMITER_MAX_NUM>;
-// attachTime, releaseTime, ratio, thresh, postGain
+// attackTime, releaseTime, ratio, thresh, postGain
 static constexpr std::array<LimiterConfigTestAdditional, 4> kLimiterConfigTestAdditionalParam = {
         {{-1, -60, -2.5, -2, -3.14},
          {-1, 60, -2.5, 2, -3.14},
          {1, -60, 2.5, -2, 3.14},
-         {1, 60, 2.5, 2, 3.14}}};
+         {1, 60, 2.5, -2, 3.14}}};
 
 using LimiterConfigTestParams =
         std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int32_t, bool, int32_t, bool,
@@ -669,15 +742,13 @@
 enum EqBandConfigTestParamName {
     EQ_BAND_INSTANCE_NAME,
     EQ_BAND_CHANNEL,
-    EQ_BAND_CHANNEL_ENABLE,
     EQ_BAND_ENABLE,
     EQ_BAND_CUT_OFF_FREQ,
     EQ_BAND_GAIN,
     EQ_BAND_STAGE_IN_USE
 };
 using EqBandConfigTestParams = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int32_t,
-                                          std::vector<DynamicsProcessing::ChannelConfig>, bool,
-                                          std::vector<std::pair<int, float>>, float, bool>;
+                                          bool, std::vector<std::pair<int, float>>, float, bool>;
 
 void fillEqBandConfig(std::vector<DynamicsProcessing::EqBandConfig>& cfgs,
                       const EqBandConfigTestParams& params) {
@@ -698,8 +769,7 @@
   public:
     DynamicsProcessingTestEqBandConfig()
         : DynamicsProcessingTestHelper(std::get<EQ_BAND_INSTANCE_NAME>(GetParam())),
-          mStageInUse(std::get<EQ_BAND_STAGE_IN_USE>(GetParam())),
-          mChannelConfig(std::get<EQ_BAND_CHANNEL_ENABLE>(GetParam())) {
+          mStageInUse(std::get<EQ_BAND_STAGE_IN_USE>(GetParam())) {
         fillEqBandConfig(mCfgs, GetParam());
     }
 
@@ -709,14 +779,18 @@
 
     std::vector<DynamicsProcessing::EqBandConfig> mCfgs;
     const bool mStageInUse;
-    const std::vector<DynamicsProcessing::ChannelConfig> mChannelConfig;
 };
 
 TEST_P(DynamicsProcessingTestEqBandConfig, SetAndGetPreEqBandConfig) {
     mEngineConfigPreset.preEqStage.inUse = mStageInUse;
     mEngineConfigPreset.preEqStage.bandCount = mCfgs.size();
     EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset));
-    EXPECT_NO_FATAL_FAILURE(addPreEqChannelConfig(mChannelConfig));
+    std::vector<DynamicsProcessing::ChannelConfig> cfgs(mChannelCount);
+    for (int i = 0; i < mChannelCount; i++) {
+        cfgs[i].channel = i;
+        cfgs[i].enable = true;
+    }
+    EXPECT_NO_FATAL_FAILURE(addPreEqChannelConfig(cfgs));
     EXPECT_NO_FATAL_FAILURE(addPreEqBandConfigs(mCfgs));
     SetAndGetDynamicsProcessingParameters();
 }
@@ -725,7 +799,12 @@
     mEngineConfigPreset.postEqStage.inUse = mStageInUse;
     mEngineConfigPreset.postEqStage.bandCount = mCfgs.size();
     EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset));
-    EXPECT_NO_FATAL_FAILURE(addPostEqChannelConfig(mChannelConfig));
+    std::vector<DynamicsProcessing::ChannelConfig> cfgs(mChannelCount);
+    for (int i = 0; i < mChannelCount; i++) {
+        cfgs[i].channel = i;
+        cfgs[i].enable = true;
+    }
+    EXPECT_NO_FATAL_FAILURE(addPostEqChannelConfig(cfgs));
     EXPECT_NO_FATAL_FAILURE(addPostEqBandConfigs(mCfgs));
     SetAndGetDynamicsProcessingParameters();
 }
@@ -776,28 +855,23 @@
 
 INSTANTIATE_TEST_SUITE_P(
         DynamicsProcessingTest, DynamicsProcessingTestEqBandConfig,
-        ::testing::Combine(
-                testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                        IFactory::descriptor, getEffectTypeUuidDynamicsProcessing())),
-                testing::Values(-1, 0, 10),  // channel ID
-                testing::ValuesIn(
-                        DynamicsProcessingTestHelper::kChannelConfigTestSet),  // channel enable
-                testing::Bool(),                                               // band enable
-                testing::ValuesIn(kBands),       // cut off frequencies
-                testing::Values(-3.14f, 3.14f),  // gain
-                testing::Bool()),                // stage in use
+        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+                                   IFactory::descriptor, getEffectTypeUuidDynamicsProcessing())),
+                           testing::Values(-1, 0, 10),      // channel ID
+                           testing::Bool(),                 // band enable
+                           testing::ValuesIn(kBands),       // cut off frequencies
+                           testing::Values(-3.14f, 3.14f),  // gain
+                           testing::Values(true)),          // stage in use
         [](const auto& info) {
             auto descriptor = std::get<EQ_BAND_INSTANCE_NAME>(info.param).second;
             std::vector<DynamicsProcessing::EqBandConfig> cfgs;
             fillEqBandConfig(cfgs, info.param);
-            std::string enable =
-                    ::android::internal::ToString(std::get<EQ_BAND_CHANNEL_ENABLE>(info.param));
             std::string bands = ::android::internal::ToString(cfgs);
             std::string stageInUse = std::to_string(std::get<EQ_BAND_STAGE_IN_USE>(info.param));
             std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
                                descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_" + enable + "_bands_" +
-                               bands + "_stageInUse_" + stageInUse;
+                               descriptor.common.id.uuid.toString() + "_bands_" + bands +
+                               "_stageInUse_" + stageInUse;
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
@@ -811,7 +885,6 @@
 enum MbcBandConfigParamName {
     MBC_BAND_INSTANCE_NAME,
     MBC_BAND_CHANNEL,
-    MBC_BAND_CHANNEL_CONFIG,
     MBC_BAND_ENABLE,
     MBC_BAND_CUTOFF_FREQ,
     MBC_BAND_STAGE_IN_USE,
@@ -831,16 +904,15 @@
 };
 using TestParamsMbcBandConfigAdditional = std::array<float, MBC_ADD_MAX_NUM>;
 
-// attachTime, releaseTime, ratio, thresh, kneeWidth, noise, expander, preGain, postGain
+// attackTime, releaseTime, ratio, thresh, kneeWidth, noise, expander, preGain, postGain
 static constexpr std::array<TestParamsMbcBandConfigAdditional, 4> kMbcBandConfigAdditionalParam = {
         {{-3, -10, -2, -2, -5, -90, -2.5, -2, -2},
          {0, 0, 0, 0, 0, 0, 0, 0, 0},
          {-3, 10, -2, 2, -5, 90, -2.5, 2, -2},
-         {3, 10, 2, 2, 5, 90, 2.5, 2, 2}}};
+         {3, 10, 2, -2, -5, 90, 2.5, 2, 2}}};
 
 using TestParamsMbcBandConfig =
-        std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int32_t,
-                   std::vector<DynamicsProcessing::ChannelConfig>, bool,
+        std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int32_t, bool,
                    std::vector<std::pair<int, float>>, bool, TestParamsMbcBandConfigAdditional>;
 
 void fillMbcBandConfig(std::vector<DynamicsProcessing::MbcBandConfig>& cfgs,
@@ -873,8 +945,7 @@
   public:
     DynamicsProcessingTestMbcBandConfig()
         : DynamicsProcessingTestHelper(std::get<MBC_BAND_INSTANCE_NAME>(GetParam())),
-          mStageInUse(std::get<MBC_BAND_STAGE_IN_USE>(GetParam())),
-          mChannelConfig(std::get<MBC_BAND_CHANNEL_CONFIG>(GetParam())) {
+          mStageInUse(std::get<MBC_BAND_STAGE_IN_USE>(GetParam())) {
         fillMbcBandConfig(mCfgs, GetParam());
     }
 
@@ -884,42 +955,41 @@
 
     std::vector<DynamicsProcessing::MbcBandConfig> mCfgs;
     const bool mStageInUse;
-    const std::vector<DynamicsProcessing::ChannelConfig> mChannelConfig;
 };
 
 TEST_P(DynamicsProcessingTestMbcBandConfig, SetAndGetMbcBandConfig) {
     mEngineConfigPreset.mbcStage.inUse = mStageInUse;
     mEngineConfigPreset.mbcStage.bandCount = mCfgs.size();
     EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset));
-    EXPECT_NO_FATAL_FAILURE(addMbcChannelConfig(mChannelConfig));
+    std::vector<DynamicsProcessing::ChannelConfig> cfgs(mChannelCount);
+    for (int i = 0; i < mChannelCount; i++) {
+        cfgs[i].channel = i;
+        cfgs[i].enable = true;
+    }
+    EXPECT_NO_FATAL_FAILURE(addMbcChannelConfig(cfgs));
     EXPECT_NO_FATAL_FAILURE(addMbcBandConfigs(mCfgs));
     SetAndGetDynamicsProcessingParameters();
 }
 
 INSTANTIATE_TEST_SUITE_P(
         DynamicsProcessingTest, DynamicsProcessingTestMbcBandConfig,
-        ::testing::Combine(
-                testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                        IFactory::descriptor, getEffectTypeUuidDynamicsProcessing())),
-                testing::Values(-1, 0, 10),  // channel count
-                testing::ValuesIn(
-                        DynamicsProcessingTestHelper::kChannelConfigTestSet),  // channel config
-                testing::Bool(),                                               // enable
-                testing::ValuesIn(kBands),                          // cut off frequencies
-                testing::Bool(),                                    // stage in use
-                testing::ValuesIn(kMbcBandConfigAdditionalParam)),  // Additional
+        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+                                   IFactory::descriptor, getEffectTypeUuidDynamicsProcessing())),
+                           testing::Values(-1, 0, 10),  // channel count
+                           testing::Bool(),             // enable
+                           testing::ValuesIn(kBands),   // cut off frequencies
+                           testing::Bool(),             // stage in use
+                           testing::ValuesIn(kMbcBandConfigAdditionalParam)),  // Additional
         [](const auto& info) {
             auto descriptor = std::get<MBC_BAND_INSTANCE_NAME>(info.param).second;
             std::vector<DynamicsProcessing::MbcBandConfig> cfgs;
             fillMbcBandConfig(cfgs, info.param);
-            std::string enable =
-                    ::android::internal::ToString(std::get<MBC_BAND_CHANNEL_CONFIG>(info.param));
             std::string mbcBands = ::android::internal::ToString(cfgs);
             std::string stageInUse = std::to_string(std::get<MBC_BAND_STAGE_IN_USE>(info.param));
             std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
                                descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_enable_" + enable +
-                               "_bands_" + mbcBands + "_stageInUse_" + stageInUse;
+                               descriptor.common.id.uuid.toString() + "_bands_" + mbcBands +
+                               "_stageInUse_" + stageInUse;
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
diff --git a/automotive/audiocontrol/1.0/default/test/fuzzer/Android.bp b/automotive/audiocontrol/1.0/default/test/fuzzer/Android.bp
index 78f5b52..4308d52 100644
--- a/automotive/audiocontrol/1.0/default/test/fuzzer/Android.bp
+++ b/automotive/audiocontrol/1.0/default/test/fuzzer/Android.bp
@@ -48,5 +48,13 @@
             "android-media-fuzzing-reports@google.com",
         ],
         componentid: 533764,
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of android.hardware.automotive.audiocontrol@1.0-service binary",
+        vector: "local_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
     },
 }
diff --git a/automotive/audiocontrol/aidl/default/audiocontrol-default.xml b/automotive/audiocontrol/aidl/default/audiocontrol-default.xml
index 3536bb9..95cd7f0 100644
--- a/automotive/audiocontrol/aidl/default/audiocontrol-default.xml
+++ b/automotive/audiocontrol/aidl/default/audiocontrol-default.xml
@@ -1,7 +1,7 @@
 <manifest version="2.0" type="device">
     <hal format="aidl">
         <name>android.hardware.automotive.audiocontrol</name>
-        <version>2</version>
+        <version>3</version>
         <fqname>IAudioControl/default</fqname>
     </hal>
 </manifest>
diff --git a/automotive/can/1.0/default/tests/fuzzer/Android.bp b/automotive/can/1.0/default/tests/fuzzer/Android.bp
index 52b43b0..de0b96f 100644
--- a/automotive/can/1.0/default/tests/fuzzer/Android.bp
+++ b/automotive/can/1.0/default/tests/fuzzer/Android.bp
@@ -50,5 +50,13 @@
             "android-media-fuzzing-reports@google.com",
         ],
         componentid: 533764,
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of android.hardware.automotive.can@1.0-service",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
     },
 }
diff --git a/automotive/evs/aidl/impl/Android.bp b/automotive/evs/aidl/impl/Android.bp
index 0b51a0c..fa44ebb 100644
--- a/automotive/evs/aidl/impl/Android.bp
+++ b/automotive/evs/aidl/impl/Android.bp
@@ -22,7 +22,7 @@
     name: "EvsHalDefaults",
     defaults: ["android.hardware.graphics.common-ndk_static"],
     static_libs: [
-        "android.hardware.automotive.evs-V1-ndk",
+        "android.hardware.automotive.evs-V2-ndk",
         "android.hardware.common-V2-ndk",
     ],
     shared_libs: [
diff --git a/automotive/evs/aidl/impl/default/Android.bp b/automotive/evs/aidl/impl/default/Android.bp
index dbe0314..70c523b 100644
--- a/automotive/evs/aidl/impl/default/Android.bp
+++ b/automotive/evs/aidl/impl/default/Android.bp
@@ -24,13 +24,56 @@
 cc_binary {
     name: "android.hardware.automotive.evs-aidl-default-service",
     defaults: ["EvsHalDefaults"],
-    local_include_dirs: ["include"],
-    vintf_fragments: ["evs-default-service.xml"],
+    vintf_fragments: ["manifest_evs-default-service.xml"],
     init_rc: ["evs-default-service.rc"],
     vendor: true,
     relative_install_path: "hw",
-    srcs: ["src/*.cpp"],
-    shared_libs: [
-        "libbinder_ndk",
+    cflags: [
+        "-DGL_GLEXT_PROTOTYPES",
+        "-DEGL_EGLEXT_PROTOTYPES",
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wthread-safety",
     ],
+    srcs: [
+        ":libgui_frame_event_aidl",
+        "src/*.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.graphics.bufferqueue@1.0",
+        "android.hardware.graphics.bufferqueue@2.0",
+        "android.hidl.token@1.0-utils",
+        "libEGL",
+        "libGLESv2",
+        "libbase",
+        "libbinder_ndk",
+        "libbufferqueueconverter",
+        "libcamera_metadata",
+        "libhardware_legacy",
+        "libhidlbase",
+        "liblog",
+        "libnativewindow",
+        "libtinyxml2",
+        "libui",
+        "libutils",
+        "libyuv",
+    ],
+    static_libs: [
+        "android.frameworks.automotive.display-V1-ndk",
+        "android.hardware.automotive.evs-V2-ndk",
+        "android.hardware.common-V2-ndk",
+        "libaidlcommonsupport",
+        "libcutils",
+    ],
+    local_include_dirs: ["include"],
+    include_dirs: ["frameworks/native/include/"],
+    required: ["evs_mock_hal_configuration.xml"],
+}
+
+prebuilt_etc {
+    name: "evs_mock_hal_configuration.xml",
+    soc_specific: true,
+    src: "resources/evs_mock_configuration.xml",
+    sub_dir: "automotive/evs",
 }
diff --git a/automotive/evs/aidl/impl/default/evs-default-service.rc b/automotive/evs/aidl/impl/default/evs-default-service.rc
index ea8e689..3da41ff 100644
--- a/automotive/evs/aidl/impl/default/evs-default-service.rc
+++ b/automotive/evs/aidl/impl/default/evs-default-service.rc
@@ -1,5 +1,8 @@
 service vendor.evs-hal-default /vendor/bin/hw/android.hardware.automotive.evs-aidl-default-service
     class early_hal
-    user automotive_evs
-    group automotive_evs
+    priority -20
+    user graphics
+    group automotive_evs camera
+    onrestart restart cardisplayproxyd
+    onrestart restart evsmanagerd
     disabled
diff --git a/automotive/evs/aidl/impl/default/evs-default-service.xml b/automotive/evs/aidl/impl/default/evs-default-service.xml
deleted file mode 100644
index 96ff9f6..0000000
--- a/automotive/evs/aidl/impl/default/evs-default-service.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<manifest version="1.0" type="device">
-    <hal format="aidl">
-        <name>android.hardware.automotive.evs</name>
-        <transport>hwbinder</transport>
-        <version>1</version>
-        <interface>
-            <name>IEvsEnumerator</name>
-            <instance>hw/0</instance>
-        </interface>
-    </hal>
-</manifest>
diff --git a/automotive/evs/aidl/impl/default/include/ConfigManager.h b/automotive/evs/aidl/impl/default/include/ConfigManager.h
new file mode 100644
index 0000000..1d5fe77
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/include/ConfigManager.h
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "ConfigManagerUtil.h"
+
+#include <aidl/android/hardware/automotive/evs/CameraParam.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <android-base/logging.h>
+#include <system/camera_metadata.h>
+
+#include <tinyxml2.h>
+
+#include <string>
+#include <string_view>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+/*
+ * Please note that this is different from what is defined in
+ * libhardware/modules/camera/3_4/metadata/types.h; this has one additional
+ * field to store a framerate.
+ */
+typedef struct {
+    int id;
+    int width;
+    int height;
+    ::aidl::android::hardware::graphics::common::PixelFormat format;
+    int type;
+    int framerate;
+} StreamConfiguration;
+
+class ConfigManager final {
+  public:
+    static std::unique_ptr<ConfigManager> Create();
+    ConfigManager(const ConfigManager&) = delete;
+    ConfigManager& operator=(const ConfigManager&) = delete;
+
+    /* Camera device's capabilities and metadata */
+    class CameraInfo {
+      public:
+        CameraInfo() : characteristics(nullptr) {}
+
+        virtual ~CameraInfo();
+
+        /* Allocate memory for camera_metadata_t */
+        bool allocate(size_t entry_cap, size_t data_cap) {
+            if (characteristics != nullptr) {
+                LOG(ERROR) << "Camera metadata is already allocated";
+                return false;
+            }
+
+            characteristics = allocate_camera_metadata(entry_cap, data_cap);
+            return characteristics != nullptr;
+        }
+
+        /*
+         * List of supported controls that the primary client can program.
+         * Paraemters are stored with its valid range
+         */
+        std::unordered_map<::aidl::android::hardware::automotive::evs::CameraParam,
+                           std::tuple<int32_t, int32_t, int32_t>>
+                controls;
+
+        /*
+         * List of supported output stream configurations.
+         */
+        std::unordered_map<int32_t, StreamConfiguration> streamConfigurations;
+
+        /*
+         * Internal storage for camera metadata.  Each entry holds a pointer to
+         * data and number of elements
+         */
+        std::unordered_map<camera_metadata_tag_t, std::pair<void*, size_t>> cameraMetadata;
+
+        /* Camera module characteristics */
+        camera_metadata_t* characteristics;
+    };
+
+    class CameraGroupInfo : public CameraInfo {
+      public:
+        CameraGroupInfo() {}
+
+        /* ID of member camera devices */
+        std::unordered_set<std::string> devices;
+
+        /* The capture operation of member camera devices are synchronized */
+        int32_t synchronized = 0;
+    };
+
+    class SystemInfo {
+      public:
+        /* number of available cameras */
+        int32_t numCameras = 0;
+    };
+
+    class DisplayInfo {
+      public:
+        /*
+         * List of supported input stream configurations.
+         */
+        std::unordered_map<int32_t, StreamConfiguration> streamConfigurations;
+    };
+
+    /*
+     * Return system information
+     *
+     * @return SystemInfo
+     *         Constant reference of SystemInfo.
+     */
+    const SystemInfo& getSystemInfo() {
+        std::unique_lock<std::mutex> lock(mConfigLock);
+        mConfigCond.wait(lock, [this] { return mIsReady; });
+        return mSystemInfo;
+    }
+
+    /*
+     * Return a list of camera identifiers
+     *
+     * This function assumes that it is not being called frequently.
+     *
+     * @return std::vector<std::string>
+     *         A vector that contains unique camera device identifiers.
+     */
+    std::vector<std::string> getCameraIdList() {
+        std::unique_lock<std::mutex> lock(mConfigLock);
+        mConfigCond.wait(lock, [this] { return mIsReady; });
+
+        std::vector<std::string> aList;
+        aList.reserve(mCameraInfo.size());
+        for (auto&& v : mCameraInfo) {
+            aList.push_back(v.first);
+        }
+
+        return aList;
+    }
+
+    /*
+     * Return a list of camera group identifiers
+     *
+     * This function assumes that it is not being called frequently.
+     *
+     * @return std::vector<std::string>
+     *         A vector that contains unique camera device identifiers.
+     */
+    std::vector<std::string> getCameraGroupIdList() {
+        std::unique_lock<std::mutex> lock(mConfigLock);
+        mConfigCond.wait(lock, [this] { return mIsReady; });
+
+        std::vector<std::string> aList;
+        aList.reserve(mCameraGroups.size());
+        for (auto&& v : mCameraGroups) {
+            aList.push_back(v.first);
+        }
+
+        return aList;
+    }
+
+    /*
+     * Return a pointer to the camera group
+     *
+     * @return CameraGroup
+     *         A pointer to a camera group identified by a given id.
+     */
+    std::unique_ptr<CameraGroupInfo>& getCameraGroupInfo(const std::string& gid) {
+        std::unique_lock<std::mutex> lock(mConfigLock);
+        mConfigCond.wait(lock, [this] { return mIsReady; });
+
+        return mCameraGroups[gid];
+    }
+
+    /*
+     * Return a camera metadata
+     *
+     * @param  cameraId
+     *         Unique camera node identifier in string
+     *
+     * @return unique_ptr<CameraInfo>
+     *         A pointer to CameraInfo that is associated with a given camera
+     *         ID.  This returns a null pointer if this does not recognize a
+     *         given camera identifier.
+     */
+    std::unique_ptr<CameraInfo>& getCameraInfo(const std::string& cameraId) noexcept {
+        std::unique_lock<std::mutex> lock(mConfigLock);
+        mConfigCond.wait(lock, [this] { return mIsReady; });
+
+        return mCameraInfo[cameraId];
+    }
+
+    /*
+     * Tell whether the configuration data is ready to be used
+     *
+     * @return bool
+     *         True if configuration data is ready to be consumed.
+     */
+    bool isReady() const { return mIsReady; }
+
+  private:
+    /* Constructors */
+    ConfigManager() : mBinaryFilePath("") {}
+
+    static std::string_view sConfigDefaultPath;
+    static std::string_view sConfigOverridePath;
+
+    /* System configuration */
+    SystemInfo mSystemInfo;
+
+    /* Internal data structure for camera device information */
+    std::unordered_map<std::string, std::unique_ptr<CameraInfo>> mCameraInfo;
+
+    /* Internal data structure for camera device information */
+    std::unordered_map<std::string, std::unique_ptr<DisplayInfo>> mDisplayInfo;
+
+    /* Camera groups are stored in <groud id, CameraGroup> hash map */
+    std::unordered_map<std::string, std::unique_ptr<CameraGroupInfo>> mCameraGroups;
+
+    /*
+     * Camera positions are stored in <position, camera id set> hash map.
+     * The position must be one of front, rear, left, and right.
+     */
+    std::unordered_map<std::string, std::unordered_set<std::string>> mCameraPosition;
+
+    /* Configuration data lock */
+    mutable std::mutex mConfigLock;
+
+    /*
+     * This condition is signalled when it completes a configuration data
+     * preparation.
+     */
+    std::condition_variable mConfigCond;
+
+    /* A path to a binary configuration file */
+    const char* mBinaryFilePath;
+
+    /* Configuration data readiness */
+    bool mIsReady = false;
+
+    /*
+     * Parse a given EVS configuration file and store the information
+     * internally.
+     *
+     * @return bool
+     *         True if it completes parsing a file successfully.
+     */
+    bool readConfigDataFromXML() noexcept;
+
+    /*
+     * read the information of the vehicle
+     *
+     * @param  aSysElem
+     *         A pointer to "system" XML element.
+     */
+    void readSystemInfo(const tinyxml2::XMLElement* const aSysElem);
+
+    /*
+     * read the information of camera devices
+     *
+     * @param  aCameraElem
+     *         A pointer to "camera" XML element that may contain multiple
+     *         "device" elements.
+     */
+    void readCameraInfo(const tinyxml2::XMLElement* const aCameraElem);
+
+    /*
+     * read display device information
+     *
+     * @param  aDisplayElem
+     *         A pointer to "display" XML element that may contain multiple
+     *         "device" elements.
+     */
+    void readDisplayInfo(const tinyxml2::XMLElement* const aDisplayElem);
+
+    /*
+     * read camera device information
+     *
+     * @param  aCamera
+     *         A pointer to CameraInfo that will be completed by this
+     *         method.
+     *         aDeviceElem
+     *         A pointer to "device" XML element that contains camera module
+     *         capability info and its characteristics.
+     *
+     * @return bool
+     *         Return false upon any failure in reading and processing camera
+     *         device information.
+     */
+    bool readCameraDeviceInfo(CameraInfo* aCamera, const tinyxml2::XMLElement* aDeviceElem);
+
+    /*
+     * read camera metadata
+     *
+     * @param  aCapElem
+     *         A pointer to "cap" XML element.
+     * @param  aCamera
+     *         A pointer to CameraInfo that is being filled by this method.
+     * @param  dataSize
+     *         Required size of memory to store camera metadata found in this
+     *         method.  This is calculated in this method and returned to the
+     *         caller for camera_metadata allocation.
+     *
+     * @return size_t
+     *         Number of camera metadata entries
+     */
+    size_t readCameraCapabilities(const tinyxml2::XMLElement* const aCapElem, CameraInfo* aCamera,
+                                  size_t& dataSize);
+
+    /*
+     * read camera metadata
+     *
+     * @param  aParamElem
+     *         A pointer to "characteristics" XML element.
+     * @param  aCamera
+     *         A pointer to CameraInfo that is being filled by this method.
+     * @param  dataSize
+     *         Required size of memory to store camera metadata found in this
+     *         method.
+     *
+     * @return size_t
+     *         Number of camera metadata entries
+     */
+    size_t readCameraMetadata(const tinyxml2::XMLElement* const aParamElem, CameraInfo* aCamera,
+                              size_t& dataSize);
+
+    /*
+     * construct camera_metadata_t from camera capabilities and metadata
+     *
+     * @param  aCamera
+     *         A pointer to CameraInfo that is being filled by this method.
+     * @param  totalEntries
+     *         Number of camera metadata entries to be added.
+     * @param  totalDataSize
+     *         Sum of sizes of camera metadata entries to be added.
+     *
+     * @return bool
+     *         False if either it fails to allocate memory for camera metadata
+     *         or its size is not large enough to add all found camera metadata
+     *         entries.
+     */
+    bool constructCameraMetadata(CameraInfo* aCamera, const size_t totalEntries,
+                                 const size_t totalDataSize);
+
+    /*
+     * Read configuration data from the binary file
+     *
+     * @return bool
+     *         True if it succeeds to read configuration data from a binary
+     *         file.
+     */
+    bool readConfigDataFromBinary();
+
+    /*
+     * Store configuration data to the file
+     *
+     * @return bool
+     *         True if it succeeds to serialize mCameraInfo to the file.
+     */
+    bool writeConfigDataToBinary();
+
+    /*
+     * debugging method to print out all XML elements and their attributes in
+     * logcat message.
+     *
+     * @param  aNode
+     *         A pointer to the root XML element to navigate.
+     * @param  prefix
+     *         A prefix to XML string.
+     */
+    void printElementNames(const tinyxml2::XMLElement* aNode, const std::string& prefix = "") const;
+};
diff --git a/automotive/evs/aidl/impl/default/include/ConfigManagerUtil.h b/automotive/evs/aidl/impl/default/include/ConfigManagerUtil.h
new file mode 100644
index 0000000..32b50d3
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/include/ConfigManagerUtil.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/automotive/evs/CameraParam.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <android-base/macros.h>
+#include <system/camera_metadata.h>
+
+#include <string>
+#include <utility>
+
+class ConfigManagerUtil final {
+  public:
+    /**
+     * Convert a given string into V4L2_CID_*
+     */
+    static bool convertToEvsCameraParam(
+            const std::string& id,
+            ::aidl::android::hardware::automotive::evs::CameraParam& camParam);
+    /**
+     * Convert a given string into android.hardware.graphics.common.PixelFormat
+     */
+    static bool convertToPixelFormat(const std::string& in,
+                                     ::aidl::android::hardware::graphics::common::PixelFormat& out);
+    /**
+     * Convert a given string into corresponding camera metadata data tag defined in
+     * system/media/camera/include/system/camera_metadata_tags.h
+     */
+    static bool convertToMetadataTag(const char* name, camera_metadata_tag& aTag);
+    /**
+     * Convert a given string into a floating value array
+     */
+    static float* convertFloatArray(const char* sz, const char* vals, size_t& count,
+                                    const char delimiter = ',');
+    /**
+     * Trim a string
+     */
+    static std::string trimString(const std::string& src, const std::string& ws = " \n\r\t\f\v");
+
+    /**
+     * Convert a given string to corresponding camera capabilities
+     */
+    static bool convertToCameraCapability(
+            const char* name, camera_metadata_enum_android_request_available_capabilities_t& cap);
+
+    DISALLOW_IMPLICIT_CONSTRUCTORS(ConfigManagerUtil);
+};
diff --git a/automotive/evs/aidl/impl/default/include/DefaultEvsEnumerator.h b/automotive/evs/aidl/impl/default/include/DefaultEvsEnumerator.h
deleted file mode 100644
index 03a578d..0000000
--- a/automotive/evs/aidl/impl/default/include/DefaultEvsEnumerator.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef android_hardware_automotive_evs_aidl_impl_evshal_include_DefaultEvsHal_H_
-#define android_hardware_automotive_evs_aidl_impl_evshal_include_DefaultEvsHal_H_
-
-#include <aidl/android/hardware/automotive/evs/BnEvsEnumerator.h>
-
-namespace aidl::android::hardware::automotive::evs::implementation {
-
-class DefaultEvsEnumerator final
-    : public ::aidl::android::hardware::automotive::evs::BnEvsEnumerator {
-    ::ndk::ScopedAStatus isHardware(bool* flag) override;
-    ::ndk::ScopedAStatus openCamera(
-            const std::string& cameraId,
-            const ::aidl::android::hardware::automotive::evs::Stream& streamConfig,
-            std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsCamera>* obj) override;
-    ::ndk::ScopedAStatus closeCamera(
-            const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsCamera>& obj)
-            override;
-    ::ndk::ScopedAStatus getCameraList(
-            std::vector<::aidl::android::hardware::automotive::evs::CameraDesc>* list) override;
-    ::ndk::ScopedAStatus getStreamList(
-            const ::aidl::android::hardware::automotive::evs::CameraDesc& desc,
-            std::vector<::aidl::android::hardware::automotive::evs::Stream>* _aidl_return) override;
-    ::ndk::ScopedAStatus openDisplay(
-            int32_t displayId,
-            std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsDisplay>* obj) override;
-    ::ndk::ScopedAStatus closeDisplay(
-            const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsDisplay>& obj)
-            override;
-    ::ndk::ScopedAStatus getDisplayIdList(std::vector<uint8_t>* list) override;
-    ::ndk::ScopedAStatus getDisplayState(
-            ::aidl::android::hardware::automotive::evs::DisplayState* state) override;
-    ::ndk::ScopedAStatus registerStatusCallback(
-            const std::shared_ptr<
-                    ::aidl::android::hardware::automotive::evs::IEvsEnumeratorStatusCallback>&
-                    callback) override;
-    ::ndk::ScopedAStatus openUltrasonicsArray(
-            const std::string& id,
-            std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsUltrasonicsArray>* obj)
-            override;
-    ::ndk::ScopedAStatus closeUltrasonicsArray(
-            const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsUltrasonicsArray>&
-                    arr) override;
-    ::ndk::ScopedAStatus getUltrasonicsArrayList(
-            std::vector<::aidl::android::hardware::automotive::evs::UltrasonicsArrayDesc>* list)
-            override;
-};
-
-}  // namespace aidl::android::hardware::automotive::evs::implementation
-
-#endif  // android_hardware_automotive_evs_aidl_impl_evshal_include_DefaultEvsHal_H_
diff --git a/automotive/evs/aidl/impl/default/include/EvsEnumerator.h b/automotive/evs/aidl/impl/default/include/EvsEnumerator.h
new file mode 100644
index 0000000..259c266
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/include/EvsEnumerator.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "ConfigManager.h"
+#include "EvsGlDisplay.h"
+#include "EvsMockCamera.h"
+
+#include <aidl/android/frameworks/automotive/display/ICarDisplayProxy.h>
+#include <aidl/android/hardware/automotive/evs/BnEvsEnumerator.h>
+#include <aidl/android/hardware/automotive/evs/CameraDesc.h>
+#include <aidl/android/hardware/automotive/evs/DeviceStatusType.h>
+#include <aidl/android/hardware/automotive/evs/IEvsCamera.h>
+#include <aidl/android/hardware/automotive/evs/IEvsEnumeratorStatusCallback.h>
+#include <aidl/android/hardware/automotive/evs/Stream.h>
+#include <utils/Thread.h>
+
+#include <atomic>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <unordered_map>
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+class EvsEnumerator final : public ::aidl::android::hardware::automotive::evs::BnEvsEnumerator {
+  public:
+    // Methods from ::aidl::android::hardware::automotive::evs::IEvsEnumerator
+    ndk::ScopedAStatus isHardware(bool* flag) override;
+    ndk::ScopedAStatus openCamera(const std::string& cameraId, const evs::Stream& streamConfig,
+                                  std::shared_ptr<evs::IEvsCamera>* obj) override;
+    ndk::ScopedAStatus closeCamera(const std::shared_ptr<evs::IEvsCamera>& obj) override;
+    ndk::ScopedAStatus getCameraList(std::vector<evs::CameraDesc>* _aidl_return) override;
+    ndk::ScopedAStatus getStreamList(const evs::CameraDesc& desc,
+                                     std::vector<evs::Stream>* _aidl_return) override;
+    ndk::ScopedAStatus openDisplay(int32_t displayId,
+                                   std::shared_ptr<evs::IEvsDisplay>* obj) override;
+    ndk::ScopedAStatus closeDisplay(const std::shared_ptr<evs::IEvsDisplay>& obj) override;
+    ndk::ScopedAStatus getDisplayIdList(std::vector<uint8_t>* list) override;
+    ndk::ScopedAStatus getDisplayState(evs::DisplayState* state) override;
+    ndk::ScopedAStatus getDisplayStateById(int32_t displayId, evs::DisplayState* state) override;
+    ndk::ScopedAStatus registerStatusCallback(
+            const std::shared_ptr<evs::IEvsEnumeratorStatusCallback>& callback) override;
+    ndk::ScopedAStatus openUltrasonicsArray(
+            const std::string& id, std::shared_ptr<evs::IEvsUltrasonicsArray>* obj) override;
+    ndk::ScopedAStatus closeUltrasonicsArray(
+            const std::shared_ptr<evs::IEvsUltrasonicsArray>& obj) override;
+    ndk::ScopedAStatus getUltrasonicsArrayList(
+            std::vector<evs::UltrasonicsArrayDesc>* list) override;
+
+    // Implementation details
+    EvsEnumerator(const std::shared_ptr<
+                  ::aidl::android::frameworks::automotive::display::ICarDisplayProxy>&
+                          proxyService);
+
+    void notifyDeviceStatusChange(const std::string_view& deviceName, evs::DeviceStatusType type);
+
+  private:
+    struct CameraRecord {
+        evs::CameraDesc desc;
+        std::weak_ptr<EvsMockCamera> activeInstance;
+
+        CameraRecord(const char* cameraId) : desc() { desc.id = cameraId; }
+    };
+
+    class ActiveDisplays {
+      public:
+        struct DisplayInfo {
+            int32_t id{-1};
+            std::weak_ptr<EvsGlDisplay> displayWeak;
+            uintptr_t internalDisplayRawAddr;
+        };
+
+        std::optional<DisplayInfo> popDisplay(int32_t id);
+
+        std::optional<DisplayInfo> popDisplay(const std::shared_ptr<IEvsDisplay>& display);
+
+        std::unordered_map<int32_t, DisplayInfo> getAllDisplays();
+
+        bool tryInsert(int32_t id, const std::shared_ptr<EvsGlDisplay>& display);
+
+      private:
+        std::mutex mMutex;
+        std::unordered_map<int32_t, DisplayInfo> mIdToDisplay GUARDED_BY(mMutex);
+        std::unordered_map<uintptr_t, int32_t> mDisplayToId GUARDED_BY(mMutex);
+    };
+
+    bool checkPermission();
+    void closeCamera_impl(const std::shared_ptr<evs::IEvsCamera>& pCamera,
+                          const std::string& cameraId);
+    ndk::ScopedAStatus getDisplayStateImpl(std::optional<int32_t> displayId,
+                                           evs::DisplayState* state);
+
+    static bool qualifyCaptureDevice(const char* deviceName);
+    static CameraRecord* findCameraById(const std::string& cameraId);
+    static void enumerateCameras();
+    static bool addCaptureDevice(const std::string& deviceName);
+    static bool removeCaptureDevice(const std::string& deviceName);
+    // Enumerate available displays and return an id of the internal display
+    static uint64_t enumerateDisplays();
+
+    static ActiveDisplays& mutableActiveDisplays();
+
+    // NOTE:  All members values are static so that all clients operate on the same state
+    //        That is to say, this is effectively a singleton despite the fact that HIDL
+    //        constructs a new instance for each client.
+    //        Because our server has a single thread in the thread pool, these values are
+    //        never accessed concurrently despite potentially having multiple instance objects
+    //        using them.
+    static std::unordered_map<std::string, CameraRecord> sCameraList;
+    // Object destructs if client dies.
+    static std::mutex sLock;                               // Mutex on shared camera device list.
+    static std::condition_variable sCameraSignal;          // Signal on camera device addition.
+    static std::unique_ptr<ConfigManager> sConfigManager;  // ConfigManager
+    static std::shared_ptr<::aidl::android::frameworks::automotive::display::ICarDisplayProxy>
+            sDisplayProxy;
+    static std::unordered_map<uint8_t, uint64_t> sDisplayPortList;
+
+    uint64_t mInternalDisplayId;
+    std::shared_ptr<evs::IEvsEnumeratorStatusCallback> mCallback;
+};
+
+}  // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/include/EvsGlDisplay.h b/automotive/evs/aidl/impl/default/include/EvsGlDisplay.h
new file mode 100644
index 0000000..ceabd9e
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/include/EvsGlDisplay.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "GlWrapper.h"
+
+#include <aidl/android/frameworks/automotive/display/ICarDisplayProxy.h>
+#include <aidl/android/hardware/automotive/evs/BnEvsDisplay.h>
+#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
+#include <aidl/android/hardware/automotive/evs/DisplayDesc.h>
+#include <aidl/android/hardware/automotive/evs/DisplayState.h>
+
+#include <thread>
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+class EvsGlDisplay final : public BnEvsDisplay {
+  public:
+    // Methods from ::aidl::android::hardware::automotive::evs::IEvsDisplay
+    // follow.
+    ndk::ScopedAStatus getDisplayInfo(evs::DisplayDesc* _aidl_return) override;
+    ndk::ScopedAStatus getDisplayState(evs::DisplayState* _aidl_return) override;
+    ndk::ScopedAStatus getTargetBuffer(evs::BufferDesc* _aidl_return) override;
+    ndk::ScopedAStatus returnTargetBufferForDisplay(const evs::BufferDesc& buffer) override;
+    ndk::ScopedAStatus setDisplayState(evs::DisplayState state) override;
+
+    // Implementation details
+    EvsGlDisplay(const std::shared_ptr<automotivedisplay::ICarDisplayProxy>& service,
+                 uint64_t displayId);
+    virtual ~EvsGlDisplay() override;
+
+    // This gets called if another caller "steals" ownership of the display
+    void forceShutdown();
+
+  private:
+    // A graphics buffer into which we'll store images.  This member variable
+    // will be protected by semaphores.
+    struct BufferRecord {
+        ::aidl::android::hardware::graphics::common::HardwareBufferDescription description;
+        buffer_handle_t handle;
+        int fingerprint;
+    } mBuffer;
+
+    // State of a rendering thread
+    enum RenderThreadStates {
+        STOPPED = 0,
+        STOPPING = 1,
+        RUN = 2,
+    };
+
+    uint64_t mDisplayId;
+    evs::DisplayDesc mInfo;
+    evs::DisplayState mRequestedState GUARDED_BY(mLock) = evs::DisplayState::NOT_VISIBLE;
+    std::shared_ptr<automotivedisplay::ICarDisplayProxy> mDisplayProxy;
+
+    GlWrapper mGlWrapper;
+    mutable std::mutex mLock;
+
+    // This tells us whether or not our buffer is in use.  Protected by
+    // semaphores.
+    bool mBufferBusy = false;
+
+    // Variables to synchronize a rendering thread w/ main and binder threads
+    std::thread mRenderThread;
+    RenderThreadStates mState GUARDED_BY(mLock) = STOPPED;
+    bool mBufferReady = false;
+    void renderFrames();
+    bool initializeGlContextLocked() REQUIRES(mLock);
+
+    std::condition_variable mBufferReadyToUse;
+    std::condition_variable mBufferReadyToRender;
+    std::condition_variable mBufferDone;
+};
+
+}  // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/include/EvsMockCamera.h b/automotive/evs/aidl/impl/default/include/EvsMockCamera.h
new file mode 100644
index 0000000..7e010a2
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/include/EvsMockCamera.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "ConfigManager.h"
+
+#include <aidl/android/hardware/automotive/evs/BnEvsCamera.h>
+#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
+#include <aidl/android/hardware/automotive/evs/CameraDesc.h>
+#include <aidl/android/hardware/automotive/evs/CameraParam.h>
+#include <aidl/android/hardware/automotive/evs/EvsResult.h>
+#include <aidl/android/hardware/automotive/evs/IEvsCameraStream.h>
+#include <aidl/android/hardware/automotive/evs/IEvsDisplay.h>
+#include <aidl/android/hardware/automotive/evs/ParameterRange.h>
+#include <aidl/android/hardware/automotive/evs/Stream.h>
+// #include <android-base/result.h>
+#include <android/hardware_buffer.h>
+#include <ui/GraphicBuffer.h>
+
+#include <functional>
+#include <thread>
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+class EvsMockCamera : public evs::BnEvsCamera {
+    // This prevents constructors from direct access while it allows this class to
+    // be instantiated via ndk::SharedRefBase::make<>.
+  private:
+    struct Sigil {
+        explicit Sigil() = default;
+    };
+
+  public:
+    // Methods from ::android::hardware::automotive::evs::IEvsCamera follow.
+    ndk::ScopedAStatus doneWithFrame(const std::vector<evs::BufferDesc>& buffers) override;
+    ndk::ScopedAStatus forcePrimaryClient(
+            const std::shared_ptr<evs::IEvsDisplay>& display) override;
+    ndk::ScopedAStatus getCameraInfo(evs::CameraDesc* _aidl_return) override;
+    ndk::ScopedAStatus getExtendedInfo(int32_t opaqueIdentifier,
+                                       std::vector<uint8_t>* value) override;
+    ndk::ScopedAStatus getIntParameter(evs::CameraParam id, std::vector<int32_t>* value) override;
+    ndk::ScopedAStatus getIntParameterRange(evs::CameraParam id,
+                                            evs::ParameterRange* _aidl_return) override;
+    ndk::ScopedAStatus getParameterList(std::vector<evs::CameraParam>* _aidl_return) override;
+    ndk::ScopedAStatus getPhysicalCameraInfo(const std::string& deviceId,
+                                             evs::CameraDesc* _aidl_return) override;
+    ndk::ScopedAStatus importExternalBuffers(const std::vector<evs::BufferDesc>& buffers,
+                                             int32_t* _aidl_return) override;
+    ndk::ScopedAStatus pauseVideoStream() override;
+    ndk::ScopedAStatus resumeVideoStream() override;
+    ndk::ScopedAStatus setExtendedInfo(int32_t opaqueIdentifier,
+                                       const std::vector<uint8_t>& opaqueValue) override;
+    ndk::ScopedAStatus setIntParameter(evs::CameraParam id, int32_t value,
+                                       std::vector<int32_t>* effectiveValue) override;
+    ndk::ScopedAStatus setPrimaryClient() override;
+    ndk::ScopedAStatus setMaxFramesInFlight(int32_t bufferCount) override;
+    ndk::ScopedAStatus startVideoStream(
+            const std::shared_ptr<evs::IEvsCameraStream>& receiver) override;
+    ndk::ScopedAStatus stopVideoStream() override;
+    ndk::ScopedAStatus unsetPrimaryClient() override;
+
+    static std::shared_ptr<EvsMockCamera> Create(const char* deviceName);
+    static std::shared_ptr<EvsMockCamera> Create(
+            const char* deviceName, std::unique_ptr<ConfigManager::CameraInfo>& camInfo,
+            const evs::Stream* streamCfg = nullptr);
+    EvsMockCamera(const EvsMockCamera&) = delete;
+    EvsMockCamera& operator=(const EvsMockCamera&) = delete;
+
+    virtual ~EvsMockCamera() override;
+    void shutdown();
+
+    const evs::CameraDesc& getDesc() { return mDescription; }
+
+    // Constructors
+    EvsMockCamera(Sigil sigil, const char* deviceName,
+                  std::unique_ptr<ConfigManager::CameraInfo>& camInfo);
+
+  private:
+    // These three functions are expected to be called while mAccessLock is held
+    bool setAvailableFrames_Locked(unsigned bufferCount);
+    unsigned increaseAvailableFrames_Locked(unsigned numToAdd);
+    unsigned decreaseAvailableFrames_Locked(unsigned numToRemove);
+
+    void generateFrames();
+    void fillMockFrame(buffer_handle_t handle, const AHardwareBuffer_Desc* pDesc);
+    void returnBufferLocked(const uint32_t bufferId);
+    ndk::ScopedAStatus stopVideoStream_impl();
+
+    CameraDesc mDescription = {};  // The properties of this camera
+
+    std::thread mCaptureThread;  // The thread we'll use to synthesize frames
+
+    // The callback used to deliver each frame
+    std::shared_ptr<evs::IEvsCameraStream> mStream;
+
+    // Horizontal pixel count in the buffers
+    uint32_t mWidth = 0;
+    // Vertical pixel count in the buffers
+    uint32_t mHeight = 0;
+    // Values from android_pixel_format_t
+    uint32_t mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+    // Values from from Gralloc.h
+    uint64_t mUsage =
+            GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_OFTEN;
+    // Bytes per line in the buffers
+    uint32_t mStride = 0;
+
+    struct BufferRecord {
+        buffer_handle_t handle;
+        bool inUse;
+
+        explicit BufferRecord(buffer_handle_t h) : handle(h), inUse(false){};
+    };
+
+    std::vector<BufferRecord> mBuffers;  // Graphics buffers to transfer images
+    unsigned mFramesAllowed;             // How many buffers are we currently using
+    unsigned mFramesInUse;               // How many buffers are currently outstanding
+
+    enum StreamStateValues {
+        STOPPED,
+        RUNNING,
+        STOPPING,
+        DEAD,
+    };
+    StreamStateValues mStreamState;
+
+    // Synchronization necessary to deconflict mCaptureThread from the main service thread
+    std::mutex mAccessLock;
+
+    // Static camera module information
+    std::unique_ptr<ConfigManager::CameraInfo>& mCameraInfo;
+
+    // For the extended info
+    std::unordered_map<uint32_t, std::vector<uint8_t>> mExtInfo;
+
+    // For the camera parameters.
+    struct CameraParameterDesc {
+        CameraParameterDesc(int min = 0, int max = 0, int step = 0, int value = 0) {
+            this->range.min = min;
+            this->range.max = max;
+            this->range.step = step;
+            this->value = value;
+        }
+
+        ParameterRange range;
+        int32_t value;
+    };
+    std::unordered_map<CameraParam, std::shared_ptr<CameraParameterDesc>> mParams;
+    void initializeParameters();
+};
+
+}  // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/include/GlWrapper.h b/automotive/evs/aidl/impl/default/include/GlWrapper.h
new file mode 100644
index 0000000..adb250c
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/include/GlWrapper.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#include <aidl/android/frameworks/automotive/display/ICarDisplayProxy.h>
+#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
+#include <android-base/logging.h>
+#include <bufferqueueconverter/BufferQueueConverter.h>
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+namespace automotivedisplay = ::aidl::android::frameworks::automotive::display;
+
+class GlWrapper {
+  public:
+    GlWrapper() : mSurfaceHolder(::android::SurfaceHolderUniquePtr(nullptr, nullptr)) {}
+    bool initialize(const std::shared_ptr<automotivedisplay::ICarDisplayProxy>& svc,
+                    uint64_t displayId);
+    void shutdown();
+
+    bool updateImageTexture(
+            buffer_handle_t handle,
+            const ::aidl::android::hardware::graphics::common::HardwareBufferDescription&
+                    description);
+    void renderImageToScreen();
+
+    void showWindow(const std::shared_ptr<automotivedisplay::ICarDisplayProxy>& svc,
+                    uint64_t displayId);
+    void hideWindow(const std::shared_ptr<automotivedisplay::ICarDisplayProxy>& svc,
+                    uint64_t displayId);
+
+    unsigned getWidth() { return mWidth; };
+    unsigned getHeight() { return mHeight; };
+
+  private:
+    ::android::sp<::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer>
+            mGfxBufferProducer;
+
+    EGLDisplay mDisplay;
+    EGLSurface mSurface;
+    EGLContext mContext;
+
+    unsigned mWidth = 0;
+    unsigned mHeight = 0;
+
+    EGLImageKHR mKHRimage = EGL_NO_IMAGE_KHR;
+
+    GLuint mTextureMap = 0;
+    GLuint mShaderProgram = 0;
+
+    // Opaque handle for a native hardware buffer defined in
+    // frameworks/native/opengl/include/EGL/eglplatform.h
+    ANativeWindow* mWindow;
+
+    // Pointer to a Surface wrapper.
+    ::android::SurfaceHolderUniquePtr mSurfaceHolder;
+};
+
+}  // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/manifest_evs-default-service.xml b/automotive/evs/aidl/impl/default/manifest_evs-default-service.xml
new file mode 100644
index 0000000..50692f7
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/manifest_evs-default-service.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.automotive.evs</name>
+        <fqname>IEvsEnumerator/hw/0</fqname>
+        <version>1</version>
+    </hal>
+</manifest>
diff --git a/automotive/evs/aidl/impl/default/resources/evs_mock_configuration.xml b/automotive/evs/aidl/impl/default/resources/evs_mock_configuration.xml
new file mode 100644
index 0000000..6cbc18e
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/resources/evs_mock_configuration.xml
@@ -0,0 +1,68 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<!-- Exterior View System Example Configuration
+
+     Android Automotive axes are used to define coordinates.
+     See https://source.android.com/devices/sensors/sensor-types#auto_axes
+
+     Use evs_configuration.dtd with xmllint tool, to validate XML configuration file
+-->
+
+<configuration>
+    <!-- system configuration -->
+    <system>
+        <!-- number of cameras available to EVS -->
+        <num_cameras value='1'/>
+    </system>
+
+    <!-- camera information -->
+    <camera>
+        <!-- camera device starts -->
+        <device id='/dev/video10' position='rear'>
+            <caps>
+                <!-- list of supported controls -->
+                <supported_controls>
+                    <control name='BRIGHTNESS' min='0' max='255'/>
+                    <control name='CONTRAST' min='0' max='255'/>
+                </supported_controls>
+
+                <stream id='0' width='640'  height='360'  format='RGBA_8888' framerate='30'/>
+            </caps>
+
+            <!-- list of parameters -->
+            <characteristics>
+                <!-- Camera intrinsic calibration matrix. See
+                     https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#LENS_INTRINSIC_CALIBRATION
+                -->
+                <parameter
+                    name='LENS_INTRINSIC_CALIBRATION'
+                    type='float'
+                    size='5'
+                    value='0.0,0.0,0.0,0.0,0.0'
+                />
+            </characteristics>
+        </device>
+    </camera>
+    <display>
+        <device id='display0' position='driver'>
+            <caps>
+                <!-- list of supported inpu stream configurations -->
+                <stream id='0' width='1280' height='720' format='RGBA_8888' framerate='30'/>
+            </caps>
+        </device>
+    </display>
+</configuration>
+
diff --git a/automotive/evs/aidl/impl/default/src/ConfigManager.cpp b/automotive/evs/aidl/impl/default/src/ConfigManager.cpp
new file mode 100644
index 0000000..da791ed
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/src/ConfigManager.cpp
@@ -0,0 +1,992 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ConfigManager.h"
+
+#include <android-base/parseint.h>
+#include <hardware/gralloc.h>
+#include <utils/SystemClock.h>
+
+#include <fstream>
+#include <sstream>
+#include <string_view>
+#include <thread>
+
+namespace {
+
+using ::aidl::android::hardware::automotive::evs::CameraParam;
+using ::aidl::android::hardware::graphics::common::PixelFormat;
+using ::tinyxml2::XMLAttribute;
+using ::tinyxml2::XMLDocument;
+using ::tinyxml2::XMLElement;
+
+}  // namespace
+
+std::string_view ConfigManager::sConfigDefaultPath =
+        "/vendor/etc/automotive/evs/evs_mock_hal_configuration.xml";
+std::string_view ConfigManager::sConfigOverridePath =
+        "/vendor/etc/automotive/evs/evs_configuration_override.xml";
+
+void ConfigManager::printElementNames(const XMLElement* rootElem, const std::string& prefix) const {
+    const XMLElement* curElem = rootElem;
+
+    while (curElem != nullptr) {
+        LOG(VERBOSE) << "[ELEM] " << prefix << curElem->Name();
+        const XMLAttribute* curAttr = curElem->FirstAttribute();
+        while (curAttr) {
+            LOG(VERBOSE) << "[ATTR] " << prefix << curAttr->Name() << ": " << curAttr->Value();
+            curAttr = curAttr->Next();
+        }
+
+        /* recursively go down to descendants */
+        printElementNames(curElem->FirstChildElement(), prefix + "\t");
+
+        curElem = curElem->NextSiblingElement();
+    }
+}
+
+void ConfigManager::readCameraInfo(const XMLElement* const aCameraElem) {
+    if (aCameraElem == nullptr) {
+        LOG(WARNING) << "XML file does not have required camera element";
+        return;
+    }
+
+    const XMLElement* curElem = aCameraElem->FirstChildElement();
+    while (curElem != nullptr) {
+        if (!strcmp(curElem->Name(), "group")) {
+            /* camera group identifier */
+            const char* id = curElem->FindAttribute("id")->Value();
+
+            /* create a camera group to be filled */
+            CameraGroupInfo* aCamera = new CameraGroupInfo();
+
+            /* read camera device information */
+            if (!readCameraDeviceInfo(aCamera, curElem)) {
+                LOG(WARNING) << "Failed to read a camera information of " << id;
+                delete aCamera;
+                continue;
+            }
+
+            /* camera group synchronization */
+            const char* sync = curElem->FindAttribute("synchronized")->Value();
+            if (!strcmp(sync, "CALIBRATED")) {
+                aCamera->synchronized = ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED;
+            } else if (!strcmp(sync, "APPROXIMATE")) {
+                aCamera->synchronized = ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE;
+            } else {
+                aCamera->synchronized = 0;  // Not synchronized
+            }
+
+            /* add a group to hash map */
+            mCameraGroups.insert_or_assign(id, std::unique_ptr<CameraGroupInfo>(aCamera));
+        } else if (!std::strcmp(curElem->Name(), "device")) {
+            /* camera unique identifier */
+            const char* id = curElem->FindAttribute("id")->Value();
+
+            /* camera mount location */
+            const char* pos = curElem->FindAttribute("position")->Value();
+
+            /* create a camera device to be filled */
+            CameraInfo* aCamera = new CameraInfo();
+
+            /* read camera device information */
+            if (!readCameraDeviceInfo(aCamera, curElem)) {
+                LOG(WARNING) << "Failed to read a camera information of " << id;
+                delete aCamera;
+                continue;
+            }
+
+            /* store read camera module information */
+            mCameraInfo.insert_or_assign(id, std::unique_ptr<CameraInfo>(aCamera));
+
+            /* assign a camera device to a position group */
+            mCameraPosition[pos].insert(id);
+        } else {
+            /* ignore other device types */
+            LOG(DEBUG) << "Unknown element " << curElem->Name() << " is ignored";
+        }
+
+        curElem = curElem->NextSiblingElement();
+    }
+}
+
+bool ConfigManager::readCameraDeviceInfo(CameraInfo* aCamera, const XMLElement* aDeviceElem) {
+    if (aCamera == nullptr || aDeviceElem == nullptr) {
+        return false;
+    }
+
+    /* size information to allocate camera_metadata_t */
+    size_t totalEntries = 0;
+    size_t totalDataSize = 0;
+
+    /* read device capabilities */
+    totalEntries +=
+            readCameraCapabilities(aDeviceElem->FirstChildElement("caps"), aCamera, totalDataSize);
+
+    /* read camera metadata */
+    totalEntries += readCameraMetadata(aDeviceElem->FirstChildElement("characteristics"), aCamera,
+                                       totalDataSize);
+
+    /* construct camera_metadata_t */
+    if (!constructCameraMetadata(aCamera, totalEntries, totalDataSize)) {
+        LOG(WARNING) << "Either failed to allocate memory or "
+                     << "allocated memory was not large enough";
+    }
+
+    return true;
+}
+
+size_t ConfigManager::readCameraCapabilities(const XMLElement* const aCapElem, CameraInfo* aCamera,
+                                             size_t& dataSize) {
+    if (aCapElem == nullptr || aCamera == nullptr) {
+        return 0;
+    }
+
+    std::string token;
+    const XMLElement* curElem = nullptr;
+
+    /* a list of supported camera parameters/controls */
+    curElem = aCapElem->FirstChildElement("supported_controls");
+    if (curElem != nullptr) {
+        const XMLElement* ctrlElem = curElem->FirstChildElement("control");
+        while (ctrlElem != nullptr) {
+            const char* nameAttr = ctrlElem->FindAttribute("name")->Value();
+            int32_t minVal = INT32_MIN, maxVal = INT32_MAX;
+            if (!android::base::ParseInt(ctrlElem->FindAttribute("min")->Value(), &minVal)) {
+                LOG(WARNING) << "Failed to parse " << ctrlElem->FindAttribute("min")->Value();
+            }
+
+            if (!android::base::ParseInt(ctrlElem->FindAttribute("max")->Value(), &maxVal)) {
+                LOG(WARNING) << "Failed to parse " << ctrlElem->FindAttribute("max")->Value();
+            }
+
+            int32_t stepVal = 1;
+            const XMLAttribute* stepAttr = ctrlElem->FindAttribute("step");
+            if (stepAttr != nullptr) {
+                if (!android::base::ParseInt(stepAttr->Value(), &stepVal)) {
+                    LOG(WARNING) << "Failed to parse " << stepAttr->Value();
+                }
+            }
+
+            CameraParam aParam;
+            if (ConfigManagerUtil::convertToEvsCameraParam(nameAttr, aParam)) {
+                aCamera->controls.insert_or_assign(
+                        aParam, std::move(std::make_tuple(minVal, maxVal, stepVal)));
+            }
+
+            ctrlElem = ctrlElem->NextSiblingElement("control");
+        }
+    }
+
+    /* a list of camera stream configurations */
+    curElem = aCapElem->FirstChildElement("stream");
+    while (curElem != nullptr) {
+        /* read 5 attributes */
+        const XMLAttribute* idAttr = curElem->FindAttribute("id");
+        const XMLAttribute* widthAttr = curElem->FindAttribute("width");
+        const XMLAttribute* heightAttr = curElem->FindAttribute("height");
+        const XMLAttribute* fmtAttr = curElem->FindAttribute("format");
+        const XMLAttribute* fpsAttr = curElem->FindAttribute("framerate");
+
+        int32_t id = -1;
+        int32_t framerate = 0;
+        if (!android::base::ParseInt(idAttr->Value(), &id)) {
+            LOG(WARNING) << "Failed to parse " << idAttr->Value();
+        }
+        if (fpsAttr != nullptr) {
+            if (!android::base::ParseInt(fpsAttr->Value(), &framerate)) {
+                LOG(WARNING) << "Failed to parse " << fpsAttr->Value();
+            }
+        }
+
+        PixelFormat format = PixelFormat::UNSPECIFIED;
+        if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), format)) {
+            StreamConfiguration cfg = {
+                    .id = id,
+                    .format = format,
+                    .type = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+                    .framerate = framerate,
+            };
+
+            if (!android::base::ParseInt(widthAttr->Value(), &cfg.width) ||
+                !android::base::ParseInt(heightAttr->Value(), &cfg.height)) {
+                LOG(WARNING) << "Failed to parse " << widthAttr->Value() << " and "
+                             << heightAttr->Value();
+            }
+            aCamera->streamConfigurations.insert_or_assign(id, cfg);
+        }
+
+        curElem = curElem->NextSiblingElement("stream");
+    }
+
+    dataSize = calculate_camera_metadata_entry_data_size(
+            get_camera_metadata_tag_type(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS),
+            aCamera->streamConfigurations.size() * sizeof(StreamConfiguration));
+
+    /* a single camera metadata entry contains multiple stream configurations */
+    return dataSize > 0 ? 1 : 0;
+}
+
+size_t ConfigManager::readCameraMetadata(const XMLElement* const aParamElem, CameraInfo* aCamera,
+                                         size_t& dataSize) {
+    if (aParamElem == nullptr || aCamera == nullptr) {
+        return 0;
+    }
+
+    const XMLElement* curElem = aParamElem->FirstChildElement("parameter");
+    size_t numEntries = 0;
+    camera_metadata_tag_t tag;
+    while (curElem != nullptr) {
+        if (ConfigManagerUtil::convertToMetadataTag(curElem->FindAttribute("name")->Value(), tag)) {
+            switch (tag) {
+                case ANDROID_LENS_DISTORTION:
+                case ANDROID_LENS_POSE_ROTATION:
+                case ANDROID_LENS_POSE_TRANSLATION:
+                case ANDROID_LENS_INTRINSIC_CALIBRATION: {
+                    /* float[] */
+                    size_t count = 0;
+                    void* data = ConfigManagerUtil::convertFloatArray(
+                            curElem->FindAttribute("size")->Value(),
+                            curElem->FindAttribute("value")->Value(), count);
+
+                    aCamera->cameraMetadata.insert_or_assign(tag, std::make_pair(data, count));
+
+                    ++numEntries;
+                    dataSize += calculate_camera_metadata_entry_data_size(
+                            get_camera_metadata_tag_type(tag), count);
+
+                    break;
+                }
+
+                case ANDROID_REQUEST_AVAILABLE_CAPABILITIES: {
+                    camera_metadata_enum_android_request_available_capabilities_t* data =
+                            new camera_metadata_enum_android_request_available_capabilities_t[1];
+                    if (ConfigManagerUtil::convertToCameraCapability(
+                                curElem->FindAttribute("value")->Value(), *data)) {
+                        aCamera->cameraMetadata.insert_or_assign(tag,
+                                                                 std::make_pair((void*)data, 1));
+
+                        ++numEntries;
+                        dataSize += calculate_camera_metadata_entry_data_size(
+                                get_camera_metadata_tag_type(tag), 1);
+                    }
+                    break;
+                }
+
+                case ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS: {
+                    /* a comma-separated list of physical camera devices */
+                    size_t len = strlen(curElem->FindAttribute("value")->Value());
+                    char* data = new char[len + 1];
+                    memcpy(data, curElem->FindAttribute("value")->Value(), len * sizeof(char));
+
+                    /* replace commas with null char */
+                    char* p = data;
+                    while (*p != '\0') {
+                        if (*p == ',') {
+                            *p = '\0';
+                        }
+                        ++p;
+                    }
+
+                    aCamera->cameraMetadata.insert_or_assign(tag,
+                                                             std::make_pair((void*)data, len + 1));
+
+                    ++numEntries;
+                    dataSize += calculate_camera_metadata_entry_data_size(
+                            get_camera_metadata_tag_type(tag), len);
+                    break;
+                }
+
+                /* TODO(b/140416878): add vendor-defined/custom tag support */
+                default:
+                    LOG(WARNING) << "Parameter " << curElem->FindAttribute("name")->Value()
+                                 << " is not supported";
+                    break;
+            }
+        } else {
+            LOG(WARNING) << "Unsupported metadata tag " << curElem->FindAttribute("name")->Value()
+                         << " is found.";
+        }
+
+        curElem = curElem->NextSiblingElement("parameter");
+    }
+
+    return numEntries;
+}
+
+bool ConfigManager::constructCameraMetadata(CameraInfo* aCamera, size_t totalEntries,
+                                            size_t totalDataSize) {
+    if (aCamera == nullptr || !aCamera->allocate(totalEntries, totalDataSize)) {
+        LOG(ERROR) << "Failed to allocate memory for camera metadata";
+        return false;
+    }
+
+    const size_t numStreamConfigs = aCamera->streamConfigurations.size();
+    std::unique_ptr<int32_t[]> data(new int32_t[sizeof(StreamConfiguration) * numStreamConfigs]);
+    int32_t* ptr = data.get();
+    for (auto& cfg : aCamera->streamConfigurations) {
+        memcpy(ptr, &cfg.second, sizeof(StreamConfiguration));
+        ptr += sizeof(StreamConfiguration);
+    }
+    int32_t err = add_camera_metadata_entry(
+            aCamera->characteristics, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, data.get(),
+            numStreamConfigs * sizeof(StreamConfiguration));
+
+    if (err) {
+        LOG(ERROR) << "Failed to add stream configurations to metadata, ignored";
+        return false;
+    }
+
+    bool success = true;
+    for (auto& [tag, entry] : aCamera->cameraMetadata) {
+        /* try to add new camera metadata entry */
+        int32_t err =
+                add_camera_metadata_entry(aCamera->characteristics, tag, entry.first, entry.second);
+        if (err) {
+            LOG(ERROR) << "Failed to add an entry with a tag, " << std::hex << tag;
+
+            /* may exceed preallocated capacity */
+            LOG(ERROR) << "Camera metadata has "
+                       << get_camera_metadata_entry_count(aCamera->characteristics) << " / "
+                       << get_camera_metadata_entry_capacity(aCamera->characteristics)
+                       << " entries and "
+                       << get_camera_metadata_data_count(aCamera->characteristics) << " / "
+                       << get_camera_metadata_data_capacity(aCamera->characteristics)
+                       << " bytes are filled.";
+            LOG(ERROR) << "\tCurrent metadata entry requires "
+                       << calculate_camera_metadata_entry_data_size(tag, entry.second) << " bytes.";
+
+            success = false;
+        }
+    }
+
+    LOG(VERBOSE) << "Camera metadata has "
+                 << get_camera_metadata_entry_count(aCamera->characteristics) << " / "
+                 << get_camera_metadata_entry_capacity(aCamera->characteristics) << " entries and "
+                 << get_camera_metadata_data_count(aCamera->characteristics) << " / "
+                 << get_camera_metadata_data_capacity(aCamera->characteristics)
+                 << " bytes are filled.";
+    return success;
+}
+
+void ConfigManager::readSystemInfo(const XMLElement* const aSysElem) {
+    if (aSysElem == nullptr) {
+        return;
+    }
+
+    /*
+     * Please note that this function assumes that a given system XML element
+     * and its child elements follow DTD.  If it does not, it will cause a
+     * segmentation fault due to the failure of finding expected attributes.
+     */
+
+    /* read number of cameras available in the system */
+    const XMLElement* xmlElem = aSysElem->FirstChildElement("num_cameras");
+    if (xmlElem != nullptr) {
+        if (!android::base::ParseInt(xmlElem->FindAttribute("value")->Value(),
+                                     &mSystemInfo.numCameras)) {
+            LOG(WARNING) << "Failed to parse " << xmlElem->FindAttribute("value")->Value();
+        }
+    }
+}
+
+void ConfigManager::readDisplayInfo(const XMLElement* const aDisplayElem) {
+    if (aDisplayElem == nullptr) {
+        LOG(WARNING) << "XML file does not have required camera element";
+        return;
+    }
+
+    const XMLElement* curDev = aDisplayElem->FirstChildElement("device");
+    while (curDev != nullptr) {
+        const char* id = curDev->FindAttribute("id")->Value();
+        std::unique_ptr<DisplayInfo> dpy(new DisplayInfo());
+        if (dpy == nullptr) {
+            LOG(ERROR) << "Failed to allocate memory for DisplayInfo";
+            return;
+        }
+
+        const XMLElement* cap = curDev->FirstChildElement("caps");
+        if (cap != nullptr) {
+            const XMLElement* curStream = cap->FirstChildElement("stream");
+            while (curStream != nullptr) {
+                /* read 4 attributes */
+                const XMLAttribute* idAttr = curStream->FindAttribute("id");
+                const XMLAttribute* widthAttr = curStream->FindAttribute("width");
+                const XMLAttribute* heightAttr = curStream->FindAttribute("height");
+                const XMLAttribute* fmtAttr = curStream->FindAttribute("format");
+
+                int32_t id = -1;
+                if (!android::base::ParseInt(idAttr->Value(), &id)) {
+                    LOG(WARNING) << "Failed to parse " << idAttr->Value();
+                }
+                PixelFormat format = PixelFormat::UNSPECIFIED;
+                if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), format)) {
+                    StreamConfiguration cfg = {
+                            .id = id,
+                            .format = format,
+                            .type = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT,
+                    };
+                    if (!android::base::ParseInt(widthAttr->Value(), &cfg.width) ||
+                        !android::base::ParseInt(heightAttr->Value(), &cfg.height)) {
+                        LOG(WARNING) << "Failed to parse " << widthAttr->Value() << " and "
+                                     << heightAttr->Value();
+                    }
+                    dpy->streamConfigurations.insert_or_assign(id, cfg);
+                }
+
+                curStream = curStream->NextSiblingElement("stream");
+            }
+        }
+
+        mDisplayInfo.insert_or_assign(id, std::move(dpy));
+        curDev = curDev->NextSiblingElement("device");
+    }
+
+    return;
+}
+
+bool ConfigManager::readConfigDataFromXML() noexcept {
+    XMLDocument xmlDoc;
+
+    const int64_t parsingStart = android::elapsedRealtimeNano();
+
+    /* load and parse a configuration file */
+    xmlDoc.LoadFile(sConfigOverridePath.data());
+    if (xmlDoc.ErrorID() != tinyxml2::XML_SUCCESS) {
+        xmlDoc.LoadFile(sConfigDefaultPath.data());
+        if (xmlDoc.ErrorID() != tinyxml2::XML_SUCCESS) {
+            LOG(ERROR) << "Failed to load and/or parse a configuration file, " << xmlDoc.ErrorStr();
+            return false;
+        }
+    }
+
+    /* retrieve the root element */
+    const XMLElement* rootElem = xmlDoc.RootElement();
+    if (std::strcmp(rootElem->Name(), "configuration") != 0) {
+        LOG(ERROR) << "A configuration file is not in the required format.  "
+                   << "See /etc/automotive/evs/evs_configuration.dtd";
+        return false;
+    }
+
+    std::unique_lock<std::mutex> lock(mConfigLock);
+
+    /*
+     * parse camera information; this needs to be done before reading system
+     * information
+     */
+    readCameraInfo(rootElem->FirstChildElement("camera"));
+
+    /* parse system information */
+    readSystemInfo(rootElem->FirstChildElement("system"));
+
+    /* parse display information */
+    readDisplayInfo(rootElem->FirstChildElement("display"));
+
+    /* configuration data is ready to be consumed */
+    mIsReady = true;
+
+    /* notify that configuration data is ready */
+    lock.unlock();
+    mConfigCond.notify_all();
+
+    const int64_t parsingEnd = android::elapsedRealtimeNano();
+    LOG(INFO) << "Parsing configuration file takes " << std::scientific
+              << (double)(parsingEnd - parsingStart) / 1000000.0 << " ms.";
+
+    return true;
+}
+
+bool ConfigManager::readConfigDataFromBinary() {
+    /* Temporary buffer to hold configuration data read from a binary file */
+    char mBuffer[1024];
+
+    std::fstream srcFile;
+    const int64_t readStart = android::elapsedRealtimeNano();
+
+    srcFile.open(mBinaryFilePath, std::fstream::in | std::fstream::binary);
+    if (!srcFile) {
+        LOG(ERROR) << "Failed to open a source binary file, " << mBinaryFilePath;
+        return false;
+    }
+
+    std::unique_lock<std::mutex> lock(mConfigLock);
+    mIsReady = false;
+
+    /* read configuration data into the internal buffer */
+    srcFile.read(mBuffer, sizeof(mBuffer));
+    LOG(VERBOSE) << __FUNCTION__ << ": " << srcFile.gcount() << " bytes are read.";
+    char* p = mBuffer;
+    size_t sz = 0;
+
+    /* read number of camera group information entries */
+    const size_t ngrps = *(reinterpret_cast<size_t*>(p));
+    p += sizeof(size_t);
+
+    /* read each camera information entry */
+    for (size_t cidx = 0; cidx < ngrps; ++cidx) {
+        /* read camera identifier */
+        std::string cameraId = *(reinterpret_cast<std::string*>(p));
+        p += sizeof(std::string);
+
+        /* size of camera_metadata_t */
+        const size_t num_entry = *(reinterpret_cast<size_t*>(p));
+        p += sizeof(size_t);
+        const size_t num_data = *(reinterpret_cast<size_t*>(p));
+        p += sizeof(size_t);
+
+        /* create CameraInfo and add it to hash map */
+        std::unique_ptr<ConfigManager::CameraGroupInfo> aCamera;
+        if (aCamera == nullptr || !aCamera->allocate(num_entry, num_data)) {
+            LOG(ERROR) << "Failed to create new CameraInfo object";
+            mCameraInfo.clear();
+            return false;
+        }
+
+        /* controls */
+        typedef struct {
+            CameraParam cid;
+            int32_t min;
+            int32_t max;
+            int32_t step;
+        } CameraCtrl;
+        sz = *(reinterpret_cast<size_t*>(p));
+        p += sizeof(size_t);
+        CameraCtrl* ptr = reinterpret_cast<CameraCtrl*>(p);
+        for (size_t idx = 0; idx < sz; ++idx) {
+            CameraCtrl temp = *ptr++;
+            aCamera->controls.insert_or_assign(
+                    temp.cid, std::move(std::make_tuple(temp.min, temp.max, temp.step)));
+        }
+        p = reinterpret_cast<char*>(ptr);
+
+        /* stream configurations */
+        sz = *(reinterpret_cast<size_t*>(p));
+        p += sizeof(size_t);
+        int32_t* i32_ptr = reinterpret_cast<int32_t*>(p);
+        for (size_t idx = 0; idx < sz; ++idx) {
+            const int32_t id = *i32_ptr++;
+
+            StreamConfiguration temp;
+            memcpy(&temp, i32_ptr, sizeof(StreamConfiguration));
+            i32_ptr += sizeof(StreamConfiguration);
+            aCamera->streamConfigurations.insert_or_assign(id, temp);
+        }
+        p = reinterpret_cast<char*>(i32_ptr);
+
+        /* synchronization */
+        aCamera->synchronized = *(reinterpret_cast<int32_t*>(p));
+        p += sizeof(int32_t);
+
+        for (size_t idx = 0; idx < num_entry; ++idx) {
+            /* Read camera metadata entries */
+            camera_metadata_tag_t tag = *reinterpret_cast<camera_metadata_tag_t*>(p);
+            p += sizeof(camera_metadata_tag_t);
+            const size_t count = *reinterpret_cast<size_t*>(p);
+            p += sizeof(size_t);
+
+            const int32_t type = get_camera_metadata_tag_type(tag);
+            switch (type) {
+                case TYPE_BYTE: {
+                    add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+                    p += count * sizeof(uint8_t);
+                    break;
+                }
+                case TYPE_INT32: {
+                    add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+                    p += count * sizeof(int32_t);
+                    break;
+                }
+                case TYPE_FLOAT: {
+                    add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+                    p += count * sizeof(float);
+                    break;
+                }
+                case TYPE_INT64: {
+                    add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+                    p += count * sizeof(int64_t);
+                    break;
+                }
+                case TYPE_DOUBLE: {
+                    add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+                    p += count * sizeof(double);
+                    break;
+                }
+                case TYPE_RATIONAL:
+                    p += count * sizeof(camera_metadata_rational_t);
+                    break;
+                default:
+                    LOG(WARNING) << "Type " << type << " is unknown; "
+                                 << "data may be corrupted.";
+                    break;
+            }
+        }
+
+        mCameraInfo.insert_or_assign(cameraId, std::move(aCamera));
+    }
+
+    /* read number of camera information entries */
+    const size_t ncams = *(reinterpret_cast<size_t*>(p));
+    p += sizeof(size_t);
+
+    /* read each camera information entry */
+    for (size_t cidx = 0; cidx < ncams; ++cidx) {
+        /* read camera identifier */
+        std::string cameraId = *(reinterpret_cast<std::string*>(p));
+        p += sizeof(std::string);
+
+        /* size of camera_metadata_t */
+        const size_t num_entry = *(reinterpret_cast<size_t*>(p));
+        p += sizeof(size_t);
+        const size_t num_data = *(reinterpret_cast<size_t*>(p));
+        p += sizeof(size_t);
+
+        /* create CameraInfo and add it to hash map */
+        std::unique_ptr<ConfigManager::CameraInfo> aCamera;
+        if (aCamera == nullptr || !aCamera->allocate(num_entry, num_data)) {
+            LOG(ERROR) << "Failed to create new CameraInfo object";
+            mCameraInfo.clear();
+            return false;
+        }
+
+        /* controls */
+        typedef struct {
+            CameraParam cid;
+            int32_t min;
+            int32_t max;
+            int32_t step;
+        } CameraCtrl;
+        sz = *(reinterpret_cast<size_t*>(p));
+        p += sizeof(size_t);
+        CameraCtrl* ptr = reinterpret_cast<CameraCtrl*>(p);
+        for (size_t idx = 0; idx < sz; ++idx) {
+            CameraCtrl temp = *ptr++;
+            aCamera->controls.insert_or_assign(
+                    temp.cid, std::move(std::make_tuple(temp.min, temp.max, temp.step)));
+        }
+        p = reinterpret_cast<char*>(ptr);
+
+        /* stream configurations */
+        sz = *(reinterpret_cast<size_t*>(p));
+        p += sizeof(size_t);
+        int32_t* i32_ptr = reinterpret_cast<int32_t*>(p);
+        for (size_t idx = 0; idx < sz; ++idx) {
+            const int32_t id = *i32_ptr++;
+
+            StreamConfiguration temp;
+            memcpy(&temp, i32_ptr, sizeof(StreamConfiguration));
+            i32_ptr += sizeof(StreamConfiguration);
+            aCamera->streamConfigurations.insert_or_assign(id, temp);
+        }
+        p = reinterpret_cast<char*>(i32_ptr);
+
+        for (size_t idx = 0; idx < num_entry; ++idx) {
+            /* Read camera metadata entries */
+            camera_metadata_tag_t tag = *reinterpret_cast<camera_metadata_tag_t*>(p);
+            p += sizeof(camera_metadata_tag_t);
+            const size_t count = *reinterpret_cast<size_t*>(p);
+            p += sizeof(size_t);
+
+            const int32_t type = get_camera_metadata_tag_type(tag);
+            switch (type) {
+                case TYPE_BYTE: {
+                    add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+                    p += count * sizeof(uint8_t);
+                    break;
+                }
+                case TYPE_INT32: {
+                    add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+                    p += count * sizeof(int32_t);
+                    break;
+                }
+                case TYPE_FLOAT: {
+                    add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+                    p += count * sizeof(float);
+                    break;
+                }
+                case TYPE_INT64: {
+                    add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+                    p += count * sizeof(int64_t);
+                    break;
+                }
+                case TYPE_DOUBLE: {
+                    add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+                    p += count * sizeof(double);
+                    break;
+                }
+                case TYPE_RATIONAL:
+                    p += count * sizeof(camera_metadata_rational_t);
+                    break;
+                default:
+                    LOG(WARNING) << "Type " << type << " is unknown; "
+                                 << "data may be corrupted.";
+                    break;
+            }
+        }
+
+        mCameraInfo.insert_or_assign(cameraId, std::move(aCamera));
+    }
+
+    mIsReady = true;
+
+    /* notify that configuration data is ready */
+    lock.unlock();
+    mConfigCond.notify_all();
+
+    int64_t readEnd = android::elapsedRealtimeNano();
+    LOG(INFO) << __FUNCTION__ << " takes " << std::scientific
+              << (double)(readEnd - readStart) / 1000000.0 << " ms.";
+
+    return true;
+}
+
+bool ConfigManager::writeConfigDataToBinary() {
+    std::fstream outFile;
+
+    const int64_t writeStart = android::elapsedRealtimeNano();
+
+    outFile.open(mBinaryFilePath, std::fstream::out | std::fstream::binary);
+    if (!outFile) {
+        LOG(ERROR) << "Failed to open a destination binary file, " << mBinaryFilePath;
+        return false;
+    }
+
+    /* lock a configuration data while it's being written to the filesystem */
+    std::lock_guard<std::mutex> lock(mConfigLock);
+
+    /* write camera group information */
+    size_t sz = mCameraGroups.size();
+    outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
+    for (auto&& [camId, camInfo] : mCameraGroups) {
+        LOG(INFO) << "Storing camera group " << camId;
+
+        /* write a camera identifier string */
+        outFile.write(reinterpret_cast<const char*>(&camId), sizeof(std::string));
+
+        /* controls */
+        sz = camInfo->controls.size();
+        outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
+        for (auto&& [ctrl, range] : camInfo->controls) {
+            outFile.write(reinterpret_cast<const char*>(&ctrl), sizeof(CameraParam));
+            outFile.write(reinterpret_cast<const char*>(&std::get<0>(range)), sizeof(int32_t));
+            outFile.write(reinterpret_cast<const char*>(&std::get<1>(range)), sizeof(int32_t));
+            outFile.write(reinterpret_cast<const char*>(&std::get<2>(range)), sizeof(int32_t));
+        }
+
+        /* stream configurations */
+        sz = camInfo->streamConfigurations.size();
+        outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
+        for (auto&& [sid, cfg] : camInfo->streamConfigurations) {
+            outFile.write(reinterpret_cast<const char*>(sid), sizeof(int32_t));
+            outFile.write(reinterpret_cast<const char*>(&cfg), sizeof(cfg));
+        }
+
+        /* synchronization */
+        outFile.write(reinterpret_cast<const char*>(&camInfo->synchronized), sizeof(int32_t));
+
+        /* size of camera_metadata_t */
+        size_t num_entry = 0;
+        size_t num_data = 0;
+        if (camInfo->characteristics != nullptr) {
+            num_entry = get_camera_metadata_entry_count(camInfo->characteristics);
+            num_data = get_camera_metadata_data_count(camInfo->characteristics);
+        }
+        outFile.write(reinterpret_cast<const char*>(&num_entry), sizeof(size_t));
+        outFile.write(reinterpret_cast<const char*>(&num_data), sizeof(size_t));
+
+        /* write each camera metadata entry */
+        if (num_entry > 0) {
+            camera_metadata_entry_t entry;
+            for (size_t idx = 0; idx < num_entry; ++idx) {
+                if (get_camera_metadata_entry(camInfo->characteristics, idx, &entry)) {
+                    LOG(ERROR) << "Failed to retrieve camera metadata entry " << idx;
+                    outFile.close();
+                    return false;
+                }
+
+                outFile.write(reinterpret_cast<const char*>(&entry.tag), sizeof(entry.tag));
+                outFile.write(reinterpret_cast<const char*>(&entry.count), sizeof(entry.count));
+
+                int32_t type = get_camera_metadata_tag_type(entry.tag);
+                switch (type) {
+                    case TYPE_BYTE:
+                        outFile.write(reinterpret_cast<const char*>(entry.data.u8),
+                                      sizeof(uint8_t) * entry.count);
+                        break;
+                    case TYPE_INT32:
+                        outFile.write(reinterpret_cast<const char*>(entry.data.i32),
+                                      sizeof(int32_t) * entry.count);
+                        break;
+                    case TYPE_FLOAT:
+                        outFile.write(reinterpret_cast<const char*>(entry.data.f),
+                                      sizeof(float) * entry.count);
+                        break;
+                    case TYPE_INT64:
+                        outFile.write(reinterpret_cast<const char*>(entry.data.i64),
+                                      sizeof(int64_t) * entry.count);
+                        break;
+                    case TYPE_DOUBLE:
+                        outFile.write(reinterpret_cast<const char*>(entry.data.d),
+                                      sizeof(double) * entry.count);
+                        break;
+                    case TYPE_RATIONAL:
+                        [[fallthrough]];
+                    default:
+                        LOG(WARNING) << "Type " << type << " is not supported.";
+                        break;
+                }
+            }
+        }
+    }
+
+    /* write camera device information */
+    sz = mCameraInfo.size();
+    outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
+    for (auto&& [camId, camInfo] : mCameraInfo) {
+        LOG(INFO) << "Storing camera " << camId;
+
+        /* write a camera identifier string */
+        outFile.write(reinterpret_cast<const char*>(&camId), sizeof(std::string));
+
+        /* controls */
+        sz = camInfo->controls.size();
+        outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
+        for (auto& [ctrl, range] : camInfo->controls) {
+            outFile.write(reinterpret_cast<const char*>(&ctrl), sizeof(CameraParam));
+            outFile.write(reinterpret_cast<const char*>(&std::get<0>(range)), sizeof(int32_t));
+            outFile.write(reinterpret_cast<const char*>(&std::get<1>(range)), sizeof(int32_t));
+            outFile.write(reinterpret_cast<const char*>(&std::get<2>(range)), sizeof(int32_t));
+        }
+
+        /* stream configurations */
+        sz = camInfo->streamConfigurations.size();
+        outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
+        for (auto&& [sid, cfg] : camInfo->streamConfigurations) {
+            outFile.write(reinterpret_cast<const char*>(sid), sizeof(int32_t));
+            outFile.write(reinterpret_cast<const char*>(&cfg), sizeof(cfg));
+        }
+
+        /* size of camera_metadata_t */
+        size_t num_entry = 0;
+        size_t num_data = 0;
+        if (camInfo->characteristics != nullptr) {
+            num_entry = get_camera_metadata_entry_count(camInfo->characteristics);
+            num_data = get_camera_metadata_data_count(camInfo->characteristics);
+        }
+        outFile.write(reinterpret_cast<const char*>(&num_entry), sizeof(size_t));
+        outFile.write(reinterpret_cast<const char*>(&num_data), sizeof(size_t));
+
+        /* write each camera metadata entry */
+        if (num_entry > 0) {
+            camera_metadata_entry_t entry;
+            for (size_t idx = 0; idx < num_entry; ++idx) {
+                if (get_camera_metadata_entry(camInfo->characteristics, idx, &entry)) {
+                    LOG(ERROR) << "Failed to retrieve camera metadata entry " << idx;
+                    outFile.close();
+                    return false;
+                }
+
+                outFile.write(reinterpret_cast<const char*>(&entry.tag), sizeof(entry.tag));
+                outFile.write(reinterpret_cast<const char*>(&entry.count), sizeof(entry.count));
+
+                int32_t type = get_camera_metadata_tag_type(entry.tag);
+                switch (type) {
+                    case TYPE_BYTE:
+                        outFile.write(reinterpret_cast<const char*>(entry.data.u8),
+                                      sizeof(uint8_t) * entry.count);
+                        break;
+                    case TYPE_INT32:
+                        outFile.write(reinterpret_cast<const char*>(entry.data.i32),
+                                      sizeof(int32_t) * entry.count);
+                        break;
+                    case TYPE_FLOAT:
+                        outFile.write(reinterpret_cast<const char*>(entry.data.f),
+                                      sizeof(float) * entry.count);
+                        break;
+                    case TYPE_INT64:
+                        outFile.write(reinterpret_cast<const char*>(entry.data.i64),
+                                      sizeof(int64_t) * entry.count);
+                        break;
+                    case TYPE_DOUBLE:
+                        outFile.write(reinterpret_cast<const char*>(entry.data.d),
+                                      sizeof(double) * entry.count);
+                        break;
+                    case TYPE_RATIONAL:
+                        [[fallthrough]];
+                    default:
+                        LOG(WARNING) << "Type " << type << " is not supported.";
+                        break;
+                }
+            }
+        }
+    }
+
+    outFile.close();
+    int64_t writeEnd = android::elapsedRealtimeNano();
+    LOG(INFO) << __FUNCTION__ << " takes " << std::scientific
+              << (double)(writeEnd - writeStart) / 1000000.0 << " ms.";
+
+    return true;
+}
+
+std::unique_ptr<ConfigManager> ConfigManager::Create() {
+    std::unique_ptr<ConfigManager> cfgMgr(new ConfigManager());
+
+    /*
+     * Read a configuration from XML file
+     *
+     * If this is too slow, ConfigManager::readConfigDataFromBinary() and
+     * ConfigManager::writeConfigDataToBinary()can serialize CameraInfo object
+     * to the filesystem and construct CameraInfo instead; this was
+     * evaluated as 10x faster.
+     */
+    if (!cfgMgr->readConfigDataFromXML()) {
+        return nullptr;
+    } else {
+        return cfgMgr;
+    }
+}
+
+ConfigManager::CameraInfo::~CameraInfo() {
+    free_camera_metadata(characteristics);
+
+    for (auto&& [tag, val] : cameraMetadata) {
+        switch (tag) {
+            case ANDROID_LENS_DISTORTION:
+            case ANDROID_LENS_POSE_ROTATION:
+            case ANDROID_LENS_POSE_TRANSLATION:
+            case ANDROID_LENS_INTRINSIC_CALIBRATION: {
+                delete[] reinterpret_cast<float*>(val.first);
+                break;
+            }
+
+            case ANDROID_REQUEST_AVAILABLE_CAPABILITIES: {
+                delete[] reinterpret_cast<
+                        camera_metadata_enum_android_request_available_capabilities_t*>(val.first);
+                break;
+            }
+
+            case ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS: {
+                delete[] reinterpret_cast<char*>(val.first);
+                break;
+            }
+
+            default:
+                LOG(WARNING) << "Tag " << std::hex << tag << " is not supported.  "
+                             << "Data may be corrupted?";
+                break;
+        }
+    }
+}
diff --git a/automotive/evs/aidl/impl/default/src/ConfigManagerUtil.cpp b/automotive/evs/aidl/impl/default/src/ConfigManagerUtil.cpp
new file mode 100644
index 0000000..e5fe6ef
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/src/ConfigManagerUtil.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ConfigManagerUtil.h"
+
+#include <android-base/logging.h>
+#include <android-base/parsedouble.h>
+#include <android-base/parseint.h>
+#include <linux/videodev2.h>
+
+#include <sstream>
+#include <string>
+
+#include <system/graphics-base-v1.0.h>
+
+using ::aidl::android::hardware::automotive::evs::CameraParam;
+using ::aidl::android::hardware::graphics::common::PixelFormat;
+
+bool ConfigManagerUtil::convertToEvsCameraParam(const std::string& id, CameraParam& camParam) {
+    std::string trimmed = ConfigManagerUtil::trimString(id);
+    bool success = true;
+
+    if (!trimmed.compare("BRIGHTNESS")) {
+        camParam = CameraParam::BRIGHTNESS;
+    } else if (!trimmed.compare("CONTRAST")) {
+        camParam = CameraParam::CONTRAST;
+    } else if (!trimmed.compare("AUTOGAIN")) {
+        camParam = CameraParam::AUTOGAIN;
+    } else if (!trimmed.compare("GAIN")) {
+        camParam = CameraParam::GAIN;
+    } else if (!trimmed.compare("AUTO_WHITE_BALANCE")) {
+        camParam = CameraParam::AUTO_WHITE_BALANCE;
+    } else if (!trimmed.compare("WHITE_BALANCE_TEMPERATURE")) {
+        camParam = CameraParam::WHITE_BALANCE_TEMPERATURE;
+    } else if (!trimmed.compare("SHARPNESS")) {
+        camParam = CameraParam::SHARPNESS;
+    } else if (!trimmed.compare("AUTO_EXPOSURE")) {
+        camParam = CameraParam::AUTO_EXPOSURE;
+    } else if (!trimmed.compare("ABSOLUTE_EXPOSURE")) {
+        camParam = CameraParam::ABSOLUTE_EXPOSURE;
+    } else if (!trimmed.compare("ABSOLUTE_FOCUS")) {
+        camParam = CameraParam::ABSOLUTE_FOCUS;
+    } else if (!trimmed.compare("AUTO_FOCUS")) {
+        camParam = CameraParam::AUTO_FOCUS;
+    } else if (!trimmed.compare("ABSOLUTE_ZOOM")) {
+        camParam = CameraParam::ABSOLUTE_ZOOM;
+    } else {
+        success = false;
+    }
+
+    return success;
+}
+
+bool ConfigManagerUtil::convertToPixelFormat(const std::string& in, PixelFormat& out) {
+    std::string trimmed = ConfigManagerUtil::trimString(in);
+    bool success = true;
+
+    if (!trimmed.compare("RGBA_8888")) {
+        out = PixelFormat::RGBA_8888;
+    } else if (!trimmed.compare("YCRCB_420_SP")) {
+        out = PixelFormat::YCRCB_420_SP;
+    } else if (!trimmed.compare("YCBCR_422_I")) {
+        out = PixelFormat::YCBCR_422_I;
+    } else {
+        out = PixelFormat::UNSPECIFIED;
+        success = false;
+    }
+
+    return success;
+}
+
+bool ConfigManagerUtil::convertToMetadataTag(const char* name, camera_metadata_tag& aTag) {
+    if (!std::strcmp(name, "LENS_DISTORTION")) {
+        aTag = ANDROID_LENS_DISTORTION;
+    } else if (!std::strcmp(name, "LENS_INTRINSIC_CALIBRATION")) {
+        aTag = ANDROID_LENS_INTRINSIC_CALIBRATION;
+    } else if (!std::strcmp(name, "LENS_POSE_ROTATION")) {
+        aTag = ANDROID_LENS_POSE_ROTATION;
+    } else if (!std::strcmp(name, "LENS_POSE_TRANSLATION")) {
+        aTag = ANDROID_LENS_POSE_TRANSLATION;
+    } else if (!std::strcmp(name, "REQUEST_AVAILABLE_CAPABILITIES")) {
+        aTag = ANDROID_REQUEST_AVAILABLE_CAPABILITIES;
+    } else if (!std::strcmp(name, "LOGICAL_MULTI_CAMERA_PHYSICAL_IDS")) {
+        aTag = ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS;
+    } else {
+        return false;
+    }
+
+    return true;
+}
+
+bool ConfigManagerUtil::convertToCameraCapability(
+        const char* name, camera_metadata_enum_android_request_available_capabilities_t& cap) {
+    if (!std::strcmp(name, "DEPTH_OUTPUT")) {
+        cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT;
+    } else if (!std::strcmp(name, "LOGICAL_MULTI_CAMERA")) {
+        cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA;
+    } else if (!std::strcmp(name, "MONOCHROME")) {
+        cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME;
+    } else if (!std::strcmp(name, "SECURE_IMAGE_DATA")) {
+        cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA;
+    } else {
+        return false;
+    }
+
+    return true;
+}
+
+float* ConfigManagerUtil::convertFloatArray(const char* sz, const char* vals, size_t& count,
+                                            const char delimiter) {
+    std::string size_string(sz);
+    std::string value_string(vals);
+
+    if (!android::base::ParseUint(size_string, &count)) {
+        LOG(ERROR) << "Failed to parse " << size_string;
+        return nullptr;
+    }
+    float* result = new float[count];
+    std::stringstream values(value_string);
+
+    int32_t idx = 0;
+    std::string token;
+    while (getline(values, token, delimiter)) {
+        if (!android::base::ParseFloat(token, &result[idx++])) {
+            LOG(WARNING) << "Failed to parse " << token;
+        }
+    }
+
+    return result;
+}
+
+std::string ConfigManagerUtil::trimString(const std::string& src, const std::string& ws) {
+    const auto s = src.find_first_not_of(ws);
+    if (s == std::string::npos) {
+        return "";
+    }
+
+    const auto e = src.find_last_not_of(ws);
+    const auto r = e - s + 1;
+
+    return src.substr(s, r);
+}
diff --git a/automotive/evs/aidl/impl/default/src/DefaultEvsEnumerator.cpp b/automotive/evs/aidl/impl/default/src/DefaultEvsEnumerator.cpp
deleted file mode 100644
index 5a81d05..0000000
--- a/automotive/evs/aidl/impl/default/src/DefaultEvsEnumerator.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// TODO(b/203661081): Remove below lines to disable compiler warnings.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-parameter"
-
-#define LOG_TAG "DefaultEvsEnumerator"
-
-#include <DefaultEvsEnumerator.h>
-
-namespace aidl::android::hardware::automotive::evs::implementation {
-
-using ::ndk::ScopedAStatus;
-
-ScopedAStatus DefaultEvsEnumerator::isHardware(bool* flag) {
-    // This returns true always.
-    *flag = true;
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::openCamera(const std::string& cameraId,
-                                               const Stream& streamConfig,
-                                               std::shared_ptr<IEvsCamera>* obj) {
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::closeCamera(const std::shared_ptr<IEvsCamera>& obj) {
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::getCameraList(std::vector<CameraDesc>* list) {
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::getStreamList(const CameraDesc& desc,
-                                                  std::vector<Stream>* _aidl_return) {
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::openDisplay(int32_t displayId,
-                                                std::shared_ptr<IEvsDisplay>* obj) {
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::closeDisplay(const std::shared_ptr<IEvsDisplay>& state) {
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::getDisplayIdList(std::vector<uint8_t>* list) {
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::getDisplayState(DisplayState* state) {
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::registerStatusCallback(
-        const std::shared_ptr<IEvsEnumeratorStatusCallback>& callback) {
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::openUltrasonicsArray(
-        const std::string& id, std::shared_ptr<IEvsUltrasonicsArray>* obj) {
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::closeUltrasonicsArray(
-        const std::shared_ptr<IEvsUltrasonicsArray>& obj) {
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::getUltrasonicsArrayList(
-        std::vector<UltrasonicsArrayDesc>* list) {
-    return ScopedAStatus::ok();
-}
-
-}  // namespace aidl::android::hardware::automotive::evs::implementation
-
-#pragma clang diagnostic pop
diff --git a/automotive/evs/aidl/impl/default/src/EvsEnumerator.cpp b/automotive/evs/aidl/impl/default/src/EvsEnumerator.cpp
new file mode 100644
index 0000000..5178958
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/src/EvsEnumerator.cpp
@@ -0,0 +1,560 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EvsEnumerator.h"
+
+#include "ConfigManager.h"
+#include "EvsGlDisplay.h"
+#include "EvsMockCamera.h"
+
+#include <aidl/android/hardware/automotive/evs/EvsResult.h>
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <cutils/android_filesystem_config.h>
+
+#include <set>
+#include <string_view>
+
+namespace {
+
+using ::aidl::android::frameworks::automotive::display::ICarDisplayProxy;
+using ::aidl::android::hardware::graphics::common::BufferUsage;
+using ::ndk::ScopedAStatus;
+using std::chrono_literals::operator""s;
+
+// Constants
+constexpr std::chrono::seconds kEnumerationTimeout = 10s;
+constexpr uint64_t kInvalidDisplayId = std::numeric_limits<uint64_t>::max();
+const std::set<uid_t> kAllowedUids = {AID_AUTOMOTIVE_EVS, AID_SYSTEM, AID_ROOT};
+
+}  // namespace
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+// NOTE:  All members values are static so that all clients operate on the same state
+//        That is to say, this is effectively a singleton despite the fact that HIDL
+//        constructs a new instance for each client.
+std::unordered_map<std::string, EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
+std::mutex EvsEnumerator::sLock;
+std::condition_variable EvsEnumerator::sCameraSignal;
+std::unique_ptr<ConfigManager> EvsEnumerator::sConfigManager;
+std::shared_ptr<ICarDisplayProxy> EvsEnumerator::sDisplayProxy;
+std::unordered_map<uint8_t, uint64_t> EvsEnumerator::sDisplayPortList;
+
+EvsEnumerator::ActiveDisplays& EvsEnumerator::mutableActiveDisplays() {
+    static ActiveDisplays active_displays;
+    return active_displays;
+}
+
+EvsEnumerator::EvsEnumerator(const std::shared_ptr<ICarDisplayProxy>& proxyService) {
+    LOG(DEBUG) << "EvsEnumerator is created.";
+
+    if (!sConfigManager) {
+        /* loads and initializes ConfigManager in a separate thread */
+        sConfigManager = ConfigManager::Create();
+    }
+
+    if (!sDisplayProxy) {
+        /* sets a car-window service handle */
+        sDisplayProxy = proxyService;
+    }
+
+    // Enumerate existing devices
+    enumerateCameras();
+    mInternalDisplayId = enumerateDisplays();
+}
+
+bool EvsEnumerator::checkPermission() {
+    const auto uid = AIBinder_getCallingUid();
+    if (kAllowedUids.find(uid) == kAllowedUids.end()) {
+        LOG(ERROR) << "EVS access denied: "
+                   << "pid = " << AIBinder_getCallingPid() << ", uid = " << uid;
+        return false;
+    }
+
+    return true;
+}
+
+void EvsEnumerator::enumerateCameras() {
+    if (!sConfigManager) {
+        return;
+    }
+
+    for (auto id : sConfigManager->getCameraIdList()) {
+        CameraRecord rec(id.data());
+        std::unique_ptr<ConfigManager::CameraInfo>& pInfo = sConfigManager->getCameraInfo(id);
+        if (pInfo) {
+            uint8_t* ptr = reinterpret_cast<uint8_t*>(pInfo->characteristics);
+            const size_t len = get_camera_metadata_size(pInfo->characteristics);
+            rec.desc.metadata.insert(rec.desc.metadata.end(), ptr, ptr + len);
+        }
+        sCameraList.insert_or_assign(id, std::move(rec));
+    }
+}
+
+uint64_t EvsEnumerator::enumerateDisplays() {
+    LOG(INFO) << __FUNCTION__ << ": Starting display enumeration";
+    uint64_t internalDisplayId = kInvalidDisplayId;
+    if (!sDisplayProxy) {
+        LOG(ERROR) << "ICarDisplayProxy is not available!";
+        return internalDisplayId;
+    }
+
+    std::vector<int64_t> displayIds;
+    if (auto status = sDisplayProxy->getDisplayIdList(&displayIds); !status.isOk()) {
+        LOG(ERROR) << "Failed to retrieve a display id list"
+                   << ::android::statusToString(status.getStatus());
+        return internalDisplayId;
+    }
+
+    if (displayIds.size() > 0) {
+        // The first entry of the list is the internal display.  See
+        // SurfaceFlinger::getPhysicalDisplayIds() implementation.
+        internalDisplayId = displayIds[0];
+        for (const auto& id : displayIds) {
+            const auto port = id & 0xFF;
+            LOG(INFO) << "Display " << std::hex << id << " is detected on the port, " << port;
+            sDisplayPortList.insert_or_assign(port, id);
+        }
+    }
+
+    LOG(INFO) << "Found " << sDisplayPortList.size() << " displays";
+    return internalDisplayId;
+}
+
+// Methods from ::android::hardware::automotive::evs::IEvsEnumerator follow.
+ScopedAStatus EvsEnumerator::getCameraList(std::vector<CameraDesc>* _aidl_return) {
+    LOG(DEBUG) << __FUNCTION__;
+    if (!checkPermission()) {
+        return ScopedAStatus::fromServiceSpecificError(
+                static_cast<int>(EvsResult::PERMISSION_DENIED));
+    }
+
+    {
+        std::unique_lock<std::mutex> lock(sLock);
+        if (sCameraList.size() < 1) {
+            // No qualified device has been found.  Wait until new device is ready,
+            // for 10 seconds.
+            if (!sCameraSignal.wait_for(lock, kEnumerationTimeout,
+                                        [] { return sCameraList.size() > 0; })) {
+                LOG(DEBUG) << "Timer expired.  No new device has been added.";
+            }
+        }
+    }
+
+    // Build up a packed array of CameraDesc for return
+    _aidl_return->resize(sCameraList.size());
+    unsigned i = 0;
+    for (const auto& [key, cam] : sCameraList) {
+        (*_aidl_return)[i++] = cam.desc;
+    }
+
+    if (sConfigManager) {
+        // Adding camera groups that represent logical camera devices
+        auto camGroups = sConfigManager->getCameraGroupIdList();
+        for (auto&& id : camGroups) {
+            if (sCameraList.find(id) != sCameraList.end()) {
+                // Already exists in the _aidl_return
+                continue;
+            }
+
+            std::unique_ptr<ConfigManager::CameraGroupInfo>& tempInfo =
+                    sConfigManager->getCameraGroupInfo(id);
+            CameraRecord cam(id.data());
+            if (tempInfo) {
+                uint8_t* ptr = reinterpret_cast<uint8_t*>(tempInfo->characteristics);
+                const size_t len = get_camera_metadata_size(tempInfo->characteristics);
+                cam.desc.metadata.insert(cam.desc.metadata.end(), ptr, ptr + len);
+            }
+
+            sCameraList.insert_or_assign(id, cam);
+            _aidl_return->push_back(cam.desc);
+        }
+    }
+
+    // Send back the results
+    LOG(DEBUG) << "Reporting " << sCameraList.size() << " cameras available";
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::getStreamList(const CameraDesc& desc,
+                                           std::vector<Stream>* _aidl_return) {
+    using AidlPixelFormat = ::aidl::android::hardware::graphics::common::PixelFormat;
+
+    camera_metadata_t* pMetadata = const_cast<camera_metadata_t*>(
+            reinterpret_cast<const camera_metadata_t*>(desc.metadata.data()));
+    camera_metadata_entry_t streamConfig;
+    if (!find_camera_metadata_entry(pMetadata, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+                                    &streamConfig)) {
+        const unsigned numStreamConfigs = streamConfig.count / sizeof(StreamConfiguration);
+        _aidl_return->resize(numStreamConfigs);
+        const StreamConfiguration* pCurrentConfig =
+                reinterpret_cast<StreamConfiguration*>(streamConfig.data.i32);
+        for (unsigned i = 0; i < numStreamConfigs; ++i, ++pCurrentConfig) {
+            // Build ::aidl::android::hardware::automotive::evs::Stream from
+            // StreamConfiguration.
+            Stream current = {
+                    .id = pCurrentConfig->id,
+                    .streamType =
+                            pCurrentConfig->type ==
+                                            ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT
+                                    ? StreamType::INPUT
+                                    : StreamType::OUTPUT,
+                    .width = pCurrentConfig->width,
+                    .height = pCurrentConfig->height,
+                    .format = static_cast<AidlPixelFormat>(pCurrentConfig->format),
+                    .usage = BufferUsage::CAMERA_INPUT,
+                    .rotation = Rotation::ROTATION_0,
+            };
+
+            (*_aidl_return)[i] = current;
+        }
+    }
+
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::openCamera(const std::string& id, const Stream& cfg,
+                                        std::shared_ptr<IEvsCamera>* obj) {
+    LOG(DEBUG) << __FUNCTION__;
+    if (!checkPermission()) {
+        return ScopedAStatus::fromServiceSpecificError(
+                static_cast<int>(EvsResult::PERMISSION_DENIED));
+    }
+
+    // Is this a recognized camera id?
+    CameraRecord* pRecord = findCameraById(id);
+    if (!pRecord) {
+        LOG(ERROR) << id << " does not exist!";
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+    }
+
+    // Has this camera already been instantiated by another caller?
+    std::shared_ptr<EvsMockCamera> pActiveCamera = pRecord->activeInstance.lock();
+    if (pActiveCamera) {
+        LOG(WARNING) << "Killing previous camera because of new caller";
+        closeCamera(pActiveCamera);
+    }
+
+    // Construct a camera instance for the caller
+    if (!sConfigManager) {
+        pActiveCamera = EvsMockCamera::Create(id.data());
+    } else {
+        pActiveCamera = EvsMockCamera::Create(id.data(), sConfigManager->getCameraInfo(id), &cfg);
+    }
+
+    pRecord->activeInstance = pActiveCamera;
+    if (!pActiveCamera) {
+        LOG(ERROR) << "Failed to create new EvsMockCamera object for " << id;
+        return ScopedAStatus::fromServiceSpecificError(
+                static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
+    }
+
+    *obj = pActiveCamera;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::closeCamera(const std::shared_ptr<IEvsCamera>& cameraObj) {
+    LOG(DEBUG) << __FUNCTION__;
+
+    if (!cameraObj) {
+        LOG(ERROR) << "Ignoring call to closeCamera with null camera ptr";
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+    }
+
+    // Get the camera id so we can find it in our list
+    CameraDesc desc;
+    auto status = cameraObj->getCameraInfo(&desc);
+    if (!status.isOk()) {
+        LOG(ERROR) << "Failed to read a camera descriptor";
+        return ScopedAStatus::fromServiceSpecificError(
+                static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
+    }
+    auto cameraId = desc.id;
+    closeCamera_impl(cameraObj, cameraId);
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::openDisplay(int32_t id, std::shared_ptr<IEvsDisplay>* displayObj) {
+    LOG(DEBUG) << __FUNCTION__;
+    if (!checkPermission()) {
+        return ScopedAStatus::fromServiceSpecificError(
+                static_cast<int>(EvsResult::PERMISSION_DENIED));
+    }
+
+    auto& displays = mutableActiveDisplays();
+
+    if (auto existing_display_search = displays.popDisplay(id)) {
+        // If we already have a display active, then we need to shut it down so we can
+        // give exclusive access to the new caller.
+        std::shared_ptr<EvsGlDisplay> pActiveDisplay = existing_display_search->displayWeak.lock();
+        if (pActiveDisplay) {
+            LOG(WARNING) << "Killing previous display because of new caller";
+            pActiveDisplay->forceShutdown();
+        }
+    }
+
+    // Create a new display interface and return it
+    uint64_t targetDisplayId = mInternalDisplayId;
+    auto it = sDisplayPortList.find(id);
+    if (it != sDisplayPortList.end()) {
+        targetDisplayId = it->second;
+    } else {
+        LOG(WARNING) << "No display is available on the port " << static_cast<int32_t>(id)
+                     << ". The main display " << mInternalDisplayId << " will be used instead";
+    }
+
+    // Create a new display interface and return it.
+    std::shared_ptr<EvsGlDisplay> pActiveDisplay =
+            ndk::SharedRefBase::make<EvsGlDisplay>(sDisplayProxy, targetDisplayId);
+
+    if (auto insert_result = displays.tryInsert(id, pActiveDisplay); !insert_result) {
+        LOG(ERROR) << "Display ID " << id << " has been used by another caller.";
+        pActiveDisplay->forceShutdown();
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::RESOURCE_BUSY));
+    }
+
+    LOG(DEBUG) << "Returning new EvsGlDisplay object " << pActiveDisplay.get();
+    *displayObj = pActiveDisplay;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::closeDisplay(const std::shared_ptr<IEvsDisplay>& obj) {
+    LOG(DEBUG) << __FUNCTION__;
+
+    auto& displays = mutableActiveDisplays();
+    const auto display_search = displays.popDisplay(obj);
+
+    if (!display_search) {
+        LOG(WARNING) << "Ignoring close of previously orphaned display - why did a client steal?";
+        return ScopedAStatus::ok();
+    }
+
+    auto pActiveDisplay = display_search->displayWeak.lock();
+
+    if (!pActiveDisplay) {
+        LOG(ERROR) << "Somehow a display is being destroyed "
+                   << "when the enumerator didn't know one existed";
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+    }
+
+    pActiveDisplay->forceShutdown();
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::getDisplayState(DisplayState* state) {
+    LOG(DEBUG) << __FUNCTION__;
+    return getDisplayStateImpl(std::nullopt, state);
+}
+
+ScopedAStatus EvsEnumerator::getDisplayStateById(int32_t displayId, DisplayState* state) {
+    LOG(DEBUG) << __FUNCTION__;
+    return getDisplayStateImpl(displayId, state);
+}
+
+ScopedAStatus EvsEnumerator::getDisplayStateImpl(std::optional<int32_t> displayId,
+                                                 DisplayState* state) {
+    if (!checkPermission()) {
+        *state = DisplayState::DEAD;
+        return ScopedAStatus::fromServiceSpecificError(
+                static_cast<int>(EvsResult::PERMISSION_DENIED));
+    }
+
+    const auto& all_displays = mutableActiveDisplays().getAllDisplays();
+
+    const auto display_search = displayId ? all_displays.find(*displayId) : all_displays.begin();
+
+    if (display_search == all_displays.end()) {
+        *state = DisplayState::NOT_OPEN;
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+    }
+
+    std::shared_ptr<IEvsDisplay> pActiveDisplay = display_search->second.displayWeak.lock();
+    if (pActiveDisplay) {
+        return pActiveDisplay->getDisplayState(state);
+    } else {
+        *state = DisplayState::NOT_OPEN;
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+    }
+}
+
+ScopedAStatus EvsEnumerator::getDisplayIdList(std::vector<uint8_t>* list) {
+    std::vector<uint8_t>& output = *list;
+    if (sDisplayPortList.size() > 0) {
+        output.resize(sDisplayPortList.size());
+        unsigned i = 0;
+        output[i++] = mInternalDisplayId & 0xFF;
+        for (const auto& [port, id] : sDisplayPortList) {
+            if (mInternalDisplayId != id) {
+                output[i++] = port;
+            }
+        }
+    }
+
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::isHardware(bool* flag) {
+    *flag = true;
+    return ScopedAStatus::ok();
+}
+
+void EvsEnumerator::notifyDeviceStatusChange(const std::string_view& deviceName,
+                                             DeviceStatusType type) {
+    std::lock_guard lock(sLock);
+    if (!mCallback) {
+        return;
+    }
+
+    std::vector<DeviceStatus> status{{.id = std::string(deviceName), .status = type}};
+    if (!mCallback->deviceStatusChanged(status).isOk()) {
+        LOG(WARNING) << "Failed to notify a device status change, name = " << deviceName
+                     << ", type = " << static_cast<int>(type);
+    }
+}
+
+ScopedAStatus EvsEnumerator::registerStatusCallback(
+        const std::shared_ptr<IEvsEnumeratorStatusCallback>& callback) {
+    std::lock_guard lock(sLock);
+    if (mCallback) {
+        LOG(INFO) << "Replacing an existing device status callback";
+    }
+    mCallback = callback;
+    return ScopedAStatus::ok();
+}
+
+void EvsEnumerator::closeCamera_impl(const std::shared_ptr<IEvsCamera>& pCamera,
+                                     const std::string& cameraId) {
+    // Find the named camera
+    CameraRecord* pRecord = findCameraById(cameraId);
+
+    // Is the display being destroyed actually the one we think is active?
+    if (!pRecord) {
+        LOG(ERROR) << "Asked to close a camera whose name isn't recognized";
+    } else {
+        std::shared_ptr<EvsMockCamera> pActiveCamera = pRecord->activeInstance.lock();
+        if (!pActiveCamera) {
+            LOG(WARNING) << "Somehow a camera is being destroyed "
+                         << "when the enumerator didn't know one existed";
+        } else if (pActiveCamera != pCamera) {
+            // This can happen if the camera was aggressively reopened,
+            // orphaning this previous instance
+            LOG(WARNING) << "Ignoring close of previously orphaned camera "
+                         << "- why did a client steal?";
+        } else {
+            // Shutdown the active camera
+            pActiveCamera->shutdown();
+        }
+    }
+
+    return;
+}
+
+EvsEnumerator::CameraRecord* EvsEnumerator::findCameraById(const std::string& cameraId) {
+    // Find the named camera
+    auto found = sCameraList.find(cameraId);
+    if (found != sCameraList.end()) {
+        // Found a match!
+        return &found->second;
+    }
+
+    // We didn't find a match
+    return nullptr;
+}
+
+std::optional<EvsEnumerator::ActiveDisplays::DisplayInfo> EvsEnumerator::ActiveDisplays::popDisplay(
+        int32_t id) {
+    std::lock_guard lck(mMutex);
+    const auto search = mIdToDisplay.find(id);
+    if (search == mIdToDisplay.end()) {
+        return std::nullopt;
+    }
+    const auto display_info = search->second;
+    mIdToDisplay.erase(search);
+    mDisplayToId.erase(display_info.internalDisplayRawAddr);
+    return display_info;
+}
+
+std::optional<EvsEnumerator::ActiveDisplays::DisplayInfo> EvsEnumerator::ActiveDisplays::popDisplay(
+        const std::shared_ptr<IEvsDisplay>& display) {
+    const auto display_ptr_val = reinterpret_cast<uintptr_t>(display.get());
+    std::lock_guard lck(mMutex);
+    const auto display_to_id_search = mDisplayToId.find(display_ptr_val);
+    if (display_to_id_search == mDisplayToId.end()) {
+        LOG(ERROR) << "Unknown display.";
+        return std::nullopt;
+    }
+    const auto id = display_to_id_search->second;
+    const auto id_to_display_search = mIdToDisplay.find(id);
+    mDisplayToId.erase(display_to_id_search);
+    if (id_to_display_search == mIdToDisplay.end()) {
+        LOG(ERROR) << "No correspsonding ID for the display, probably orphaned.";
+        return std::nullopt;
+    }
+    const auto display_info = id_to_display_search->second;
+    mIdToDisplay.erase(id);
+    return display_info;
+}
+
+std::unordered_map<int32_t, EvsEnumerator::ActiveDisplays::DisplayInfo>
+EvsEnumerator::ActiveDisplays::getAllDisplays() {
+    std::lock_guard lck(mMutex);
+    auto id_to_display_map_copy = mIdToDisplay;
+    return id_to_display_map_copy;
+}
+
+bool EvsEnumerator::ActiveDisplays::tryInsert(int32_t id,
+                                              const std::shared_ptr<EvsGlDisplay>& display) {
+    std::lock_guard lck(mMutex);
+    const auto display_ptr_val = reinterpret_cast<uintptr_t>(display.get());
+
+    auto id_to_display_insert_result =
+            mIdToDisplay.emplace(id, DisplayInfo{
+                                             .id = id,
+                                             .displayWeak = display,
+                                             .internalDisplayRawAddr = display_ptr_val,
+                                     });
+    if (!id_to_display_insert_result.second) {
+        return false;
+    }
+    auto display_to_id_insert_result = mDisplayToId.emplace(display_ptr_val, id);
+    if (!display_to_id_insert_result.second) {
+        mIdToDisplay.erase(id);
+        return false;
+    }
+    return true;
+}
+
+ScopedAStatus EvsEnumerator::getUltrasonicsArrayList(
+        [[maybe_unused]] std::vector<UltrasonicsArrayDesc>* list) {
+    // TODO(b/149874793): Add implementation for EVS Manager and Sample driver
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::openUltrasonicsArray(
+        [[maybe_unused]] const std::string& id,
+        [[maybe_unused]] std::shared_ptr<IEvsUltrasonicsArray>* obj) {
+    // TODO(b/149874793): Add implementation for EVS Manager and Sample driver
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::closeUltrasonicsArray(
+        [[maybe_unused]] const std::shared_ptr<IEvsUltrasonicsArray>& obj) {
+    // TODO(b/149874793): Add implementation for EVS Manager and Sample driver
+    return ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/src/EvsGlDisplay.cpp b/automotive/evs/aidl/impl/default/src/EvsGlDisplay.cpp
new file mode 100644
index 0000000..e5f8e4c
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/src/EvsGlDisplay.cpp
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EvsGlDisplay.h"
+
+#include <aidl/android/hardware/automotive/evs/EvsResult.h>
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <aidlcommonsupport/NativeHandle.h>
+#include <android-base/thread_annotations.h>
+#include <linux/time.h>
+#include <ui/DisplayMode.h>
+#include <ui/DisplayState.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
+#include <utils/SystemClock.h>
+
+#include <chrono>
+
+namespace {
+
+using ::aidl::android::frameworks::automotive::display::ICarDisplayProxy;
+using ::aidl::android::hardware::graphics::common::BufferUsage;
+using ::aidl::android::hardware::graphics::common::PixelFormat;
+using ::android::base::ScopedLockAssertion;
+using ::ndk::ScopedAStatus;
+
+constexpr auto kTimeout = std::chrono::seconds(1);
+
+bool debugFirstFrameDisplayed = false;
+
+int generateFingerPrint(buffer_handle_t handle) {
+    return static_cast<int>(reinterpret_cast<long>(handle) & 0xFFFFFFFF);
+}
+
+}  // namespace
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+EvsGlDisplay::EvsGlDisplay(const std::shared_ptr<ICarDisplayProxy>& pDisplayProxy,
+                           uint64_t displayId)
+    : mDisplayId(displayId), mDisplayProxy(pDisplayProxy) {
+    LOG(DEBUG) << "EvsGlDisplay instantiated";
+
+    // Set up our self description
+    // NOTE:  These are arbitrary values chosen for testing
+    mInfo.id = std::to_string(displayId);
+    mInfo.vendorFlags = 3870;
+
+    // Start a thread to render images on this display
+    {
+        std::lock_guard lock(mLock);
+        mState = RUN;
+    }
+    mRenderThread = std::thread([this]() { renderFrames(); });
+}
+
+EvsGlDisplay::~EvsGlDisplay() {
+    LOG(DEBUG) << "EvsGlDisplay being destroyed";
+    forceShutdown();
+}
+
+/**
+ * This gets called if another caller "steals" ownership of the display
+ */
+void EvsGlDisplay::forceShutdown() {
+    LOG(DEBUG) << "EvsGlDisplay forceShutdown";
+    {
+        std::lock_guard lock(mLock);
+
+        // If the buffer isn't being held by a remote client, release it now as an
+        // optimization to release the resources more quickly than the destructor might
+        // get called.
+        if (mBuffer.handle != nullptr) {
+            // Report if we're going away while a buffer is outstanding
+            if (mBufferBusy || mState == RUN) {
+                LOG(ERROR) << "EvsGlDisplay going down while client is holding a buffer";
+            }
+            mState = STOPPING;
+        }
+
+        // Put this object into an unrecoverable error state since somebody else
+        // is going to own the display now.
+        mRequestedState = DisplayState::DEAD;
+    }
+    mBufferReadyToRender.notify_all();
+
+    if (mRenderThread.joinable()) {
+        mRenderThread.join();
+    }
+}
+
+/**
+ * Initialize GL in the context of a caller's thread and prepare a graphic
+ * buffer to use.
+ */
+bool EvsGlDisplay::initializeGlContextLocked() {
+    // Initialize our display window
+    // NOTE:  This will cause the display to become "VISIBLE" before a frame is actually
+    // returned, which is contrary to the spec and will likely result in a black frame being
+    // (briefly) shown.
+    if (!mGlWrapper.initialize(mDisplayProxy, mDisplayId)) {
+        // Report the failure
+        LOG(ERROR) << "Failed to initialize GL display";
+        return false;
+    }
+
+    // Assemble the buffer description we'll use for our render target
+    static_assert(::aidl::android::hardware::graphics::common::PixelFormat::RGBA_8888 ==
+                  static_cast<::aidl::android::hardware::graphics::common::PixelFormat>(
+                          HAL_PIXEL_FORMAT_RGBA_8888));
+    mBuffer.description = {
+            .width = static_cast<int>(mGlWrapper.getWidth()),
+            .height = static_cast<int>(mGlWrapper.getHeight()),
+            .layers = 1,
+            .format = PixelFormat::RGBA_8888,
+            // FIXME: Below line is not using
+            // ::aidl::android::hardware::graphics::common::BufferUsage because
+            // BufferUsage enum does not support a bitwise-OR operation; they
+            // should be BufferUsage::GPU_RENDER_TARGET |
+            // BufferUsage::COMPOSER_OVERLAY
+            .usage = static_cast<BufferUsage>(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER),
+    };
+
+    ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
+    uint32_t stride = static_cast<uint32_t>(mBuffer.description.stride);
+    buffer_handle_t handle = nullptr;
+    const ::android::status_t result =
+            alloc.allocate(mBuffer.description.width, mBuffer.description.height,
+                           static_cast<::android::PixelFormat>(mBuffer.description.format),
+                           mBuffer.description.layers,
+                           static_cast<uint64_t>(mBuffer.description.usage), &handle, &stride,
+                           /* requestorName= */ "EvsGlDisplay");
+    mBuffer.description.stride = stride;
+    mBuffer.fingerprint = generateFingerPrint(mBuffer.handle);
+    if (result != ::android::NO_ERROR) {
+        LOG(ERROR) << "Error " << result << " allocating " << mBuffer.description.width << " x "
+                   << mBuffer.description.height << " graphics buffer.";
+        mGlWrapper.shutdown();
+        return false;
+    }
+
+    mBuffer.handle = handle;
+    if (mBuffer.handle == nullptr) {
+        LOG(ERROR) << "We didn't get a buffer handle back from the allocator";
+        mGlWrapper.shutdown();
+        return false;
+    }
+
+    LOG(DEBUG) << "Allocated new buffer " << mBuffer.handle << " with stride "
+               << mBuffer.description.stride;
+    return true;
+}
+
+/**
+ * This method runs in a separate thread and renders the contents of the buffer.
+ */
+void EvsGlDisplay::renderFrames() {
+    {
+        std::lock_guard lock(mLock);
+
+        if (!initializeGlContextLocked()) {
+            LOG(ERROR) << "Failed to initialize GL context";
+            return;
+        }
+
+        // Display buffer is ready.
+        mBufferBusy = false;
+    }
+    mBufferReadyToUse.notify_all();
+
+    while (true) {
+        {
+            std::unique_lock lock(mLock);
+            ScopedLockAssertion lock_assertion(mLock);
+            mBufferReadyToRender.wait(
+                    lock, [this]() REQUIRES(mLock) { return mBufferReady || mState != RUN; });
+            if (mState != RUN) {
+                LOG(DEBUG) << "A rendering thread is stopping";
+                break;
+            }
+            mBufferReady = false;
+        }
+
+        // Update the texture contents with the provided data
+        if (!mGlWrapper.updateImageTexture(mBuffer.handle, mBuffer.description)) {
+            LOG(WARNING) << "Failed to update the image texture";
+            continue;
+        }
+
+        // Put the image on the screen
+        mGlWrapper.renderImageToScreen();
+        if (!debugFirstFrameDisplayed) {
+            LOG(DEBUG) << "EvsFirstFrameDisplayTiming start time: " << ::android::elapsedRealtime()
+                       << " ms.";
+            debugFirstFrameDisplayed = true;
+        }
+
+        // Mark current frame is consumed.
+        {
+            std::lock_guard lock(mLock);
+            mBufferBusy = false;
+        }
+        mBufferDone.notify_all();
+    }
+
+    LOG(DEBUG) << "A rendering thread is stopped.";
+
+    // Drop the graphics buffer we've been using
+    ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
+    alloc.free(mBuffer.handle);
+    mBuffer.handle = nullptr;
+
+    mGlWrapper.hideWindow(mDisplayProxy, mDisplayId);
+    mGlWrapper.shutdown();
+
+    std::lock_guard lock(mLock);
+    mState = STOPPED;
+}
+
+/**
+ * Returns basic information about the EVS display provided by the system.
+ * See the description of the DisplayDesc structure for details.
+ */
+ScopedAStatus EvsGlDisplay::getDisplayInfo(DisplayDesc* _aidl_return) {
+    if (!mDisplayProxy) {
+        return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
+    }
+
+    ::aidl::android::frameworks::automotive::display::DisplayDesc proxyDisplay;
+    auto status = mDisplayProxy->getDisplayInfo(mDisplayId, &proxyDisplay);
+    if (!status.isOk()) {
+        return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
+    }
+
+    _aidl_return->width = proxyDisplay.width;
+    _aidl_return->height = proxyDisplay.height;
+    _aidl_return->orientation = static_cast<Rotation>(proxyDisplay.orientation);
+    _aidl_return->id = mInfo.id;  // FIXME: what should be ID here?
+    _aidl_return->vendorFlags = mInfo.vendorFlags;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+/**
+ * Clients may set the display state to express their desired state.
+ * The HAL implementation must gracefully accept a request for any state
+ * while in any other state, although the response may be to ignore the request.
+ * The display is defined to start in the NOT_VISIBLE state upon initialization.
+ * The client is then expected to request the VISIBLE_ON_NEXT_FRAME state, and
+ * then begin providing video.  When the display is no longer required, the client
+ * is expected to request the NOT_VISIBLE state after passing the last video frame.
+ */
+ScopedAStatus EvsGlDisplay::setDisplayState(DisplayState state) {
+    LOG(DEBUG) << __FUNCTION__;
+    std::lock_guard lock(mLock);
+
+    if (mRequestedState == DisplayState::DEAD) {
+        // This object no longer owns the display -- it's been superceeded!
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+    }
+
+    // Ensure we recognize the requested state so we don't go off the rails
+    static constexpr ::ndk::enum_range<DisplayState> kDisplayStateRange;
+    if (std::find(kDisplayStateRange.begin(), kDisplayStateRange.end(), state) ==
+        kDisplayStateRange.end()) {
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+    }
+
+    switch (state) {
+        case DisplayState::NOT_VISIBLE:
+            mGlWrapper.hideWindow(mDisplayProxy, mDisplayId);
+            break;
+        case DisplayState::VISIBLE:
+            mGlWrapper.showWindow(mDisplayProxy, mDisplayId);
+            break;
+        default:
+            break;
+    }
+
+    // Record the requested state
+    mRequestedState = state;
+
+    return ScopedAStatus::ok();
+}
+
+/**
+ * The HAL implementation should report the actual current state, which might
+ * transiently differ from the most recently requested state.  Note, however, that
+ * the logic responsible for changing display states should generally live above
+ * the device layer, making it undesirable for the HAL implementation to
+ * spontaneously change display states.
+ */
+ScopedAStatus EvsGlDisplay::getDisplayState(DisplayState* _aidl_return) {
+    LOG(DEBUG) << __FUNCTION__;
+    std::lock_guard lock(mLock);
+    *_aidl_return = mRequestedState;
+    return ScopedAStatus::ok();
+}
+
+/**
+ * This call returns a handle to a frame buffer associated with the display.
+ * This buffer may be locked and written to by software and/or GL.  This buffer
+ * must be returned via a call to returnTargetBufferForDisplay() even if the
+ * display is no longer visible.
+ */
+ScopedAStatus EvsGlDisplay::getTargetBuffer(BufferDesc* _aidl_return) {
+    LOG(DEBUG) << __FUNCTION__;
+    std::unique_lock lock(mLock);
+    ScopedLockAssertion lock_assertion(mLock);
+    if (mRequestedState == DisplayState::DEAD) {
+        LOG(ERROR) << "Rejecting buffer request from object that lost ownership of the display.";
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+    }
+
+    // If we don't already have a buffer, allocate one now
+    // mBuffer.memHandle is a type of buffer_handle_t, which is equal to
+    // native_handle_t*.
+    mBufferReadyToUse.wait(lock, [this]() REQUIRES(mLock) { return !mBufferBusy; });
+
+    // Do we have a frame available?
+    if (mBufferBusy) {
+        // This means either we have a 2nd client trying to compete for buffers
+        // (an unsupported mode of operation) or else the client hasn't returned
+        // a previously issued buffer yet (they're behaving badly).
+        // NOTE:  We have to make the callback even if we have nothing to provide
+        LOG(ERROR) << "getTargetBuffer called while no buffers available.";
+        return ScopedAStatus::fromServiceSpecificError(
+                static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
+    }
+
+    // Mark our buffer as busy
+    mBufferBusy = true;
+
+    // Send the buffer to the client
+    LOG(VERBOSE) << "Providing display buffer handle " << mBuffer.handle;
+
+    BufferDesc bufferDescToSend = {
+            .buffer =
+                    {
+                            .handle = std::move(::android::dupToAidl(mBuffer.handle)),
+                            .description = mBuffer.description,
+                    },
+            .pixelSizeBytes = 4,  // RGBA_8888 is 4-byte-per-pixel format
+            .bufferId = mBuffer.fingerprint,
+    };
+    *_aidl_return = std::move(bufferDescToSend);
+
+    return ScopedAStatus::ok();
+}
+
+/**
+ * This call tells the display that the buffer is ready for display.
+ * The buffer is no longer valid for use by the client after this call.
+ */
+ScopedAStatus EvsGlDisplay::returnTargetBufferForDisplay(const BufferDesc& buffer) {
+    LOG(VERBOSE) << __FUNCTION__;
+    std::unique_lock lock(mLock);
+    ScopedLockAssertion lock_assertion(mLock);
+
+    // Nobody should call us with a null handle
+    if (buffer.buffer.handle.fds.size() < 1) {
+        LOG(ERROR) << __FUNCTION__ << " called without a valid buffer handle.";
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+    }
+    if (buffer.bufferId != mBuffer.fingerprint) {
+        LOG(ERROR) << "Got an unrecognized frame returned.";
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+    }
+    if (!mBufferBusy) {
+        LOG(ERROR) << "A frame was returned with no outstanding frames.";
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+    }
+
+    // If we've been displaced by another owner of the display, then we can't do anything else
+    if (mRequestedState == DisplayState::DEAD) {
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+    }
+
+    // If we were waiting for a new frame, this is it!
+    if (mRequestedState == DisplayState::VISIBLE_ON_NEXT_FRAME) {
+        mRequestedState = DisplayState::VISIBLE;
+        mGlWrapper.showWindow(mDisplayProxy, mDisplayId);
+    }
+
+    // Validate we're in an expected state
+    if (mRequestedState != DisplayState::VISIBLE) {
+        // Not sure why a client would send frames back when we're not visible.
+        LOG(WARNING) << "Got a frame returned while not visible - ignoring.";
+        return ScopedAStatus::ok();
+    }
+    mBufferReady = true;
+    mBufferReadyToRender.notify_all();
+
+    if (!mBufferDone.wait_for(lock, kTimeout, [this]() REQUIRES(mLock) { return !mBufferBusy; })) {
+        return ScopedAStatus::fromServiceSpecificError(
+                static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
+    }
+
+    return ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp b/automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp
new file mode 100644
index 0000000..797b221
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp
@@ -0,0 +1,735 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EvsMockCamera.h"
+#include "ConfigManager.h"
+#include "EvsEnumerator.h"
+
+#include <aidlcommonsupport/NativeHandle.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
+#include <utils/SystemClock.h>
+
+#include <memory>
+
+namespace {
+
+using ::aidl::android::hardware::graphics::common::BufferUsage;
+using ::ndk::ScopedAStatus;
+
+// Arbitrary limit on number of graphics buffers allowed to be allocated
+// Safeguards against unreasonable resource consumption and provides a testable limit
+constexpr unsigned kMaxBuffersInFlight = 100;
+
+// Minimum number of buffers to run a video stream
+constexpr int kMinimumBuffersInFlight = 1;
+
+// Colors for the colorbar test pattern in ABGR format
+constexpr uint32_t kColors[] = {
+        0xFFFFFFFF,  // white
+        0xFF00FFFF,  // yellow
+        0xFFFFFF00,  // cyan
+        0xFF00FF00,  // green
+        0xFFFF00FF,  // fuchsia
+        0xFF0000FF,  // red
+        0xFFFF0000,  // blue
+        0xFF000000,  // black
+};
+constexpr size_t kNumColors = sizeof(kColors) / sizeof(kColors[0]);
+
+}  // namespace
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+EvsMockCamera::EvsMockCamera([[maybe_unused]] Sigil sigil, const char* id,
+                             std::unique_ptr<ConfigManager::CameraInfo>& camInfo)
+    : mFramesAllowed(0), mFramesInUse(0), mStreamState(STOPPED), mCameraInfo(camInfo) {
+    LOG(DEBUG) << __FUNCTION__;
+
+    /* set a camera id */
+    mDescription.id = id;
+
+    /* set camera metadata */
+    if (camInfo) {
+        uint8_t* ptr = reinterpret_cast<uint8_t*>(camInfo->characteristics);
+        const size_t len = get_camera_metadata_size(camInfo->characteristics);
+        mDescription.metadata.insert(mDescription.metadata.end(), ptr, ptr + len);
+    }
+
+    // Initialize parameters.
+    initializeParameters();
+}
+
+EvsMockCamera::~EvsMockCamera() {
+    LOG(DEBUG) << __FUNCTION__;
+    shutdown();
+}
+
+void EvsMockCamera::initializeParameters() {
+    mParams.emplace(
+            CameraParam::BRIGHTNESS,
+            new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
+    mParams.emplace(
+            CameraParam::CONTRAST,
+            new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
+    mParams.emplace(
+            CameraParam::SHARPNESS,
+            new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
+}
+
+// This gets called if another caller "steals" ownership of the camera
+void EvsMockCamera::shutdown() {
+    LOG(DEBUG) << __FUNCTION__;
+
+    // Make sure our output stream is cleaned up
+    // (It really should be already)
+    stopVideoStream_impl();
+
+    // Claim the lock while we work on internal state
+    std::lock_guard lock(mAccessLock);
+
+    // Drop all the graphics buffers we've been using
+    if (mBuffers.size() > 0) {
+        ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
+        for (auto&& rec : mBuffers) {
+            if (rec.inUse) {
+                LOG(WARNING) << "WARNING: releasing a buffer remotely owned.";
+            }
+            alloc.free(rec.handle);
+            rec.handle = nullptr;
+        }
+        mBuffers.clear();
+    }
+
+    // Put this object into an unrecoverable error state since somebody else
+    // is going to own the underlying camera now
+    mStreamState = DEAD;
+}
+
+// Methods from ::aidl::android::hardware::automotive::evs::IEvsCamera follow.
+ScopedAStatus EvsMockCamera::getCameraInfo(CameraDesc* _aidl_return) {
+    LOG(DEBUG) << __FUNCTION__;
+
+    // Send back our self description
+    *_aidl_return = mDescription;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::setMaxFramesInFlight(int32_t bufferCount) {
+    LOG(DEBUG) << __FUNCTION__ << ", bufferCount = " << bufferCount;
+    ;
+
+    std::lock_guard lock(mAccessLock);
+
+    // If we've been displaced by another owner of the camera, then we can't do anything else
+    if (mStreamState == DEAD) {
+        LOG(ERROR) << "Ignoring setMaxFramesInFlight call when camera has been lost.";
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+    }
+
+    // We cannot function without at least one video buffer to send data
+    if (bufferCount < 1) {
+        LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested.";
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+    }
+
+    // Update our internal state
+    if (!setAvailableFrames_Locked(bufferCount)) {
+        LOG(ERROR) << "Failed to adjust the maximum number of frames in flight.";
+        return ScopedAStatus::fromServiceSpecificError(
+                static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
+    }
+
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::startVideoStream(const std::shared_ptr<IEvsCameraStream>& cb) {
+    LOG(DEBUG) << __FUNCTION__;
+
+    if (!cb) {
+        LOG(ERROR) << "A given stream callback is invalid.";
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+    }
+
+    std::lock_guard lock(mAccessLock);
+
+    // If we've been displaced by another owner of the camera, then we can't do anything else
+    if (mStreamState == DEAD) {
+        LOG(ERROR) << "Ignoring startVideoStream call when camera has been lost.";
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+    }
+
+    if (mStreamState != STOPPED) {
+        LOG(ERROR) << "Ignoring startVideoStream call when a stream is already running.";
+        return ScopedAStatus::fromServiceSpecificError(
+                static_cast<int>(EvsResult::STREAM_ALREADY_RUNNING));
+    }
+
+    // If the client never indicated otherwise, configure ourselves for a single streaming buffer
+    if (mFramesAllowed < kMinimumBuffersInFlight &&
+        !setAvailableFrames_Locked(kMinimumBuffersInFlight)) {
+        LOG(ERROR) << "Failed to start stream because we couldn't get a graphics buffer";
+        return ScopedAStatus::fromServiceSpecificError(
+                static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
+    }
+
+    // Record the user's callback for use when we have a frame ready
+    mStream = cb;
+
+    // Start the frame generation thread
+    mStreamState = RUNNING;
+    mCaptureThread = std::thread([this]() { generateFrames(); });
+
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::doneWithFrame(const std::vector<BufferDesc>& list) {
+    std::lock_guard lock(mAccessLock);
+    for (const auto& desc : list) {
+        returnBufferLocked(desc.bufferId);
+    }
+
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::stopVideoStream() {
+    LOG(DEBUG) << __FUNCTION__;
+    return stopVideoStream_impl();
+}
+
+ScopedAStatus EvsMockCamera::stopVideoStream_impl() {
+    std::unique_lock lock(mAccessLock);
+
+    if (mStreamState != RUNNING) {
+        // Safely return here because a stream is not running.
+        return ScopedAStatus::ok();
+    }
+
+    // Tell the GenerateFrames loop we want it to stop
+    mStreamState = STOPPING;
+
+    // Block outside the mutex until the "stop" flag has been acknowledged
+    // We won't send any more frames, but the client might still get some already in flight
+    LOG(DEBUG) << "Waiting for stream thread to end...";
+    lock.unlock();
+    if (mCaptureThread.joinable()) {
+        mCaptureThread.join();
+    }
+    lock.lock();
+
+    mStreamState = STOPPED;
+    mStream = nullptr;
+    LOG(DEBUG) << "Stream marked STOPPED.";
+
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::getExtendedInfo(int32_t opaqueIdentifier,
+                                             std::vector<uint8_t>* opaqueValue) {
+    const auto it = mExtInfo.find(opaqueIdentifier);
+    if (it == mExtInfo.end()) {
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+    } else {
+        *opaqueValue = mExtInfo[opaqueIdentifier];
+    }
+
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::setExtendedInfo(int32_t opaqueIdentifier,
+                                             const std::vector<uint8_t>& opaqueValue) {
+    mExtInfo.insert_or_assign(opaqueIdentifier, opaqueValue);
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::getPhysicalCameraInfo([[maybe_unused]] const std::string& id,
+                                                   CameraDesc* _aidl_return) {
+    LOG(DEBUG) << __FUNCTION__;
+
+    // This method works exactly same as getCameraInfo() in EVS HW module.
+    *_aidl_return = mDescription;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::pauseVideoStream() {
+    return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
+}
+
+ScopedAStatus EvsMockCamera::resumeVideoStream() {
+    return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
+}
+
+ScopedAStatus EvsMockCamera::setPrimaryClient() {
+    /* Because EVS HW module reference implementation expects a single client at
+     * a time, this returns a success code always.
+     */
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::forcePrimaryClient(const std::shared_ptr<IEvsDisplay>&) {
+    /* Because EVS HW module reference implementation expects a single client at
+     * a time, this returns a success code always.
+     */
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::unsetPrimaryClient() {
+    /* Because EVS HW module reference implementation expects a single client at
+     * a time, there is no chance that this is called by the secondary client and
+     * therefore returns a success code always.
+     */
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::getParameterList(std::vector<CameraParam>* _aidl_return) {
+    if (mCameraInfo) {
+        _aidl_return->resize(mCameraInfo->controls.size());
+        auto idx = 0;
+        for (auto& [name, range] : mCameraInfo->controls) {
+            (*_aidl_return)[idx++] = name;
+        }
+    }
+
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::getIntParameterRange(CameraParam id, ParameterRange* _aidl_return) {
+    auto it = mParams.find(id);
+    if (it == mParams.end()) {
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
+    }
+
+    _aidl_return->min = it->second->range.min;
+    _aidl_return->max = it->second->range.max;
+    _aidl_return->step = it->second->range.step;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::setIntParameter(CameraParam id, int32_t value,
+                                             std::vector<int32_t>* effectiveValue) {
+    auto it = mParams.find(id);
+    if (it == mParams.end()) {
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
+    }
+
+    // Rounding down to the closest value.
+    int32_t candidate = value / it->second->range.step * it->second->range.step;
+    if (candidate < it->second->range.min || candidate > it->second->range.max) {
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+    }
+
+    it->second->value = candidate;
+    effectiveValue->push_back(candidate);
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::getIntParameter(CameraParam id, std::vector<int32_t>* value) {
+    auto it = mParams.find(id);
+    if (it == mParams.end()) {
+        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
+    }
+
+    value->push_back(it->second->value);
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::importExternalBuffers(const std::vector<BufferDesc>& buffers,
+                                                   int32_t* _aidl_return) {
+    size_t numBuffersToAdd = buffers.size();
+    if (numBuffersToAdd < 1) {
+        LOG(DEBUG) << "Ignoring a request to import external buffers with an empty list.";
+        return ScopedAStatus::ok();
+    }
+
+    std::lock_guard lock(mAccessLock);
+    if (numBuffersToAdd > (kMaxBuffersInFlight - mFramesAllowed)) {
+        numBuffersToAdd -= (kMaxBuffersInFlight - mFramesAllowed);
+        LOG(WARNING) << "Exceed the limit on the number of buffers. " << numBuffersToAdd
+                     << " buffers will be imported only.";
+    }
+
+    ::android::GraphicBufferMapper& mapper = ::android::GraphicBufferMapper::get();
+    const size_t before = mFramesAllowed;
+    for (size_t i = 0; i < numBuffersToAdd; ++i) {
+        auto& b = buffers[i];
+        const AHardwareBuffer_Desc* pDesc =
+                reinterpret_cast<const AHardwareBuffer_Desc*>(&b.buffer.description);
+
+        buffer_handle_t handleToImport = ::android::dupFromAidl(b.buffer.handle);
+        buffer_handle_t handleToStore = nullptr;
+        if (handleToImport == nullptr) {
+            LOG(WARNING) << "Failed to duplicate a memory handle. Ignoring a buffer " << b.bufferId;
+            continue;
+        }
+
+        ::android::status_t result =
+                mapper.importBuffer(handleToImport, pDesc->width, pDesc->height, pDesc->layers,
+                                    pDesc->format, pDesc->usage, pDesc->stride, &handleToStore);
+        if (result != ::android::NO_ERROR || handleToStore == nullptr) {
+            LOG(WARNING) << "Failed to import a buffer " << b.bufferId;
+            continue;
+        }
+
+        bool stored = false;
+        for (auto&& rec : mBuffers) {
+            if (rec.handle != nullptr) {
+                continue;
+            }
+
+            // Use this existing entry.
+            rec.handle = handleToStore;
+            rec.inUse = false;
+            stored = true;
+            break;
+        }
+
+        if (!stored) {
+            // Add a BufferRecord wrapping this handle to our set of available buffers.
+            mBuffers.push_back(BufferRecord(handleToStore));
+        }
+        ++mFramesAllowed;
+    }
+
+    *_aidl_return = mFramesAllowed - before;
+    return ScopedAStatus::ok();
+}
+
+bool EvsMockCamera::setAvailableFrames_Locked(unsigned bufferCount) {
+    if (bufferCount < 1) {
+        LOG(ERROR) << "Ignoring request to set buffer count to zero";
+        return false;
+    }
+    if (bufferCount > kMaxBuffersInFlight) {
+        LOG(ERROR) << "Rejecting buffer request in excess of internal limit";
+        return false;
+    }
+
+    // Is an increase required?
+    if (mFramesAllowed < bufferCount) {
+        // An increase is required
+        auto needed = bufferCount - mFramesAllowed;
+        LOG(INFO) << "Allocating " << needed << " buffers for camera frames";
+
+        auto added = increaseAvailableFrames_Locked(needed);
+        if (added != needed) {
+            // If we didn't add all the frames we needed, then roll back to the previous state
+            LOG(ERROR) << "Rolling back to previous frame queue size";
+            decreaseAvailableFrames_Locked(added);
+            return false;
+        }
+    } else if (mFramesAllowed > bufferCount) {
+        // A decrease is required
+        auto framesToRelease = mFramesAllowed - bufferCount;
+        LOG(INFO) << "Returning " << framesToRelease << " camera frame buffers";
+
+        auto released = decreaseAvailableFrames_Locked(framesToRelease);
+        if (released != framesToRelease) {
+            // This shouldn't happen with a properly behaving client because the client
+            // should only make this call after returning sufficient outstanding buffers
+            // to allow a clean resize.
+            LOG(ERROR) << "Buffer queue shrink failed -- too many buffers currently in use?";
+        }
+    }
+
+    return true;
+}
+
+unsigned EvsMockCamera::increaseAvailableFrames_Locked(unsigned numToAdd) {
+    // Acquire the graphics buffer allocator
+    ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
+
+    unsigned added = 0;
+    while (added < numToAdd) {
+        unsigned pixelsPerLine = 0;
+        buffer_handle_t memHandle = nullptr;
+        auto result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, &memHandle,
+                                     &pixelsPerLine, 0, "EvsMockCamera");
+        if (result != ::android::NO_ERROR) {
+            LOG(ERROR) << "Error " << result << " allocating " << mWidth << " x " << mHeight
+                       << " graphics buffer";
+            break;
+        }
+        if (memHandle == nullptr) {
+            LOG(ERROR) << "We didn't get a buffer handle back from the allocator";
+            break;
+        }
+        if (mStride > 0) {
+            if (mStride != pixelsPerLine) {
+                LOG(ERROR) << "We did not expect to get buffers with different strides!";
+            }
+        } else {
+            // Gralloc defines stride in terms of pixels per line
+            mStride = pixelsPerLine;
+        }
+
+        // Find a place to store the new buffer
+        auto stored = false;
+        for (auto&& rec : mBuffers) {
+            if (rec.handle == nullptr) {
+                // Use this existing entry
+                rec.handle = memHandle;
+                rec.inUse = false;
+                stored = true;
+                break;
+            }
+        }
+        if (!stored) {
+            // Add a BufferRecord wrapping this handle to our set of available buffers
+            mBuffers.push_back(BufferRecord(memHandle));
+        }
+
+        ++mFramesAllowed;
+        ++added;
+    }
+
+    return added;
+}
+
+unsigned EvsMockCamera::decreaseAvailableFrames_Locked(unsigned numToRemove) {
+    // Acquire the graphics buffer allocator
+    ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
+
+    unsigned removed = 0;
+    for (auto&& rec : mBuffers) {
+        // Is this record not in use, but holding a buffer that we can free?
+        if ((rec.inUse == false) && (rec.handle != nullptr)) {
+            // Release buffer and update the record so we can recognize it as "empty"
+            alloc.free(rec.handle);
+            rec.handle = nullptr;
+
+            --mFramesAllowed;
+            ++removed;
+
+            if (removed == numToRemove) {
+                break;
+            }
+        }
+    }
+
+    return removed;
+}
+
+// This is the asynchronous frame generation thread that runs in parallel with the
+// main serving thread.  There is one for each active camera instance.
+void EvsMockCamera::generateFrames() {
+    LOG(DEBUG) << "Frame generation loop started.";
+
+    unsigned idx = 0;
+    while (true) {
+        bool timeForFrame = false;
+        const nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+        // Lock scope for updating shared state
+        {
+            std::lock_guard lock(mAccessLock);
+
+            if (mStreamState != RUNNING) {
+                // Break out of our main thread loop
+                break;
+            }
+
+            // Are we allowed to issue another buffer?
+            if (mFramesInUse >= mFramesAllowed) {
+                // Can't do anything right now -- skip this frame
+                LOG(WARNING) << "Skipped a frame because too many are in flight.";
+            } else {
+                // Identify an available buffer to fill
+                for (idx = 0; idx < mBuffers.size(); idx++) {
+                    if (!mBuffers[idx].inUse) {
+                        if (mBuffers[idx].handle != nullptr) {
+                            // Found an available record, so stop looking
+                            break;
+                        }
+                    }
+                }
+                if (idx >= mBuffers.size()) {
+                    // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
+                    ALOGE("Failed to find an available buffer slot\n");
+                } else {
+                    // We're going to make the frame busy
+                    mBuffers[idx].inUse = true;
+                    mFramesInUse++;
+                    timeForFrame = true;
+                }
+            }
+        }
+
+        if (timeForFrame) {
+            using AidlPixelFormat = ::aidl::android::hardware::graphics::common::PixelFormat;
+
+            // Assemble the buffer description we'll transmit below
+            buffer_handle_t memHandle = mBuffers[idx].handle;
+            BufferDesc newBuffer = {
+                    .buffer =
+                            {
+                                    .description =
+                                            {
+                                                    .width = static_cast<int32_t>(mWidth),
+                                                    .height = static_cast<int32_t>(mHeight),
+                                                    .layers = 1,
+                                                    .format = static_cast<AidlPixelFormat>(mFormat),
+                                                    .usage = static_cast<BufferUsage>(mUsage),
+                                                    .stride = static_cast<int32_t>(mStride),
+                                            },
+                                    .handle = ::android::dupToAidl(memHandle),
+                            },
+                    .bufferId = static_cast<int32_t>(idx),
+                    .deviceId = mDescription.id,
+                    .timestamp = static_cast<int64_t>(::android::elapsedRealtimeNano() *
+                                                      1e+3),  // timestamps is in microseconds
+            };
+
+            // Write test data into the image buffer
+            fillMockFrame(memHandle, reinterpret_cast<const AHardwareBuffer_Desc*>(
+                                             &newBuffer.buffer.description));
+
+            // Issue the (asynchronous) callback to the client -- can't be holding the lock
+            auto flag = false;
+            if (mStream) {
+                std::vector<BufferDesc> frames;
+                frames.push_back(std::move(newBuffer));
+                flag = mStream->deliverFrame(frames).isOk();
+            }
+
+            if (flag) {
+                LOG(DEBUG) << "Delivered " << memHandle << ", id = " << mBuffers[idx].handle;
+            } else {
+                // This can happen if the client dies and is likely unrecoverable.
+                // To avoid consuming resources generating failing calls, we stop sending
+                // frames.  Note, however, that the stream remains in the "STREAMING" state
+                // until cleaned up on the main thread.
+                LOG(ERROR) << "Frame delivery call failed in the transport layer.";
+
+                // Since we didn't actually deliver it, mark the frame as available
+                std::lock_guard<std::mutex> lock(mAccessLock);
+                mBuffers[idx].inUse = false;
+                mFramesInUse--;
+            }
+        }
+
+        // We arbitrarily choose to generate frames at 15 fps to ensure we pass the 10fps test
+        // requirement
+        static const int kTargetFrameRate = 15;
+        static const nsecs_t kTargetFrameIntervalUs = 1000 * 1000 / kTargetFrameRate;
+        const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+        const nsecs_t elapsedTimeUs = (now - startTime) / 1000;
+        const nsecs_t sleepDurationUs = kTargetFrameIntervalUs - elapsedTimeUs;
+        if (sleepDurationUs > 0) {
+            usleep(sleepDurationUs);
+        }
+    }
+
+    // If we've been asked to stop, send an event to signal the actual end of stream
+    EvsEventDesc event = {
+            .aType = EvsEventType::STREAM_STOPPED,
+    };
+    if (!mStream->notify(event).isOk()) {
+        ALOGE("Error delivering end of stream marker");
+    }
+
+    return;
+}
+
+void EvsMockCamera::fillMockFrame(buffer_handle_t handle, const AHardwareBuffer_Desc* pDesc) {
+    // Lock our output buffer for writing
+    uint32_t* pixels = nullptr;
+    ::android::GraphicBufferMapper& mapper = ::android::GraphicBufferMapper::get();
+    mapper.lock(handle, GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
+                ::android::Rect(pDesc->width, pDesc->height), (void**)&pixels);
+
+    // If we failed to lock the pixel buffer, we're about to crash, but log it first
+    if (!pixels) {
+        ALOGE("Camera failed to gain access to image buffer for writing");
+        return;
+    }
+
+    // Fill in the test pixels; the colorbar in ABGR format
+    for (unsigned row = 0; row < pDesc->height; row++) {
+        for (unsigned col = 0; col < pDesc->width; col++) {
+            const uint32_t index = col * kNumColors / pDesc->width;
+            pixels[col] = kColors[index];
+        }
+        // Point to the next row
+        // NOTE:  stride retrieved from gralloc is in units of pixels
+        pixels = pixels + pDesc->stride;
+    }
+
+    // Release our output buffer
+    mapper.unlock(handle);
+}
+
+void EvsMockCamera::returnBufferLocked(const uint32_t bufferId) {
+    if (bufferId >= mBuffers.size()) {
+        ALOGE("ignoring doneWithFrame called with invalid bufferId %d (max is %zu)", bufferId,
+              mBuffers.size() - 1);
+        return;
+    }
+
+    if (!mBuffers[bufferId].inUse) {
+        ALOGE("ignoring doneWithFrame called on frame %d which is already free", bufferId);
+        return;
+    }
+
+    // Mark the frame as available
+    mBuffers[bufferId].inUse = false;
+    mFramesInUse--;
+
+    // If this frame's index is high in the array, try to move it down
+    // to improve locality after mFramesAllowed has been reduced.
+    if (bufferId >= mFramesAllowed) {
+        // Find an empty slot lower in the array (which should always exist in this case)
+        for (auto&& rec : mBuffers) {
+            if (rec.handle == nullptr) {
+                rec.handle = mBuffers[bufferId].handle;
+                mBuffers[bufferId].handle = nullptr;
+                break;
+            }
+        }
+    }
+}
+
+std::shared_ptr<EvsMockCamera> EvsMockCamera::Create(const char* deviceName) {
+    std::unique_ptr<ConfigManager::CameraInfo> nullCamInfo = nullptr;
+
+    return Create(deviceName, nullCamInfo);
+}
+
+std::shared_ptr<EvsMockCamera> EvsMockCamera::Create(
+        const char* deviceName, std::unique_ptr<ConfigManager::CameraInfo>& camInfo,
+        [[maybe_unused]] const Stream* streamCfg) {
+    std::shared_ptr<EvsMockCamera> c =
+            ndk::SharedRefBase::make<EvsMockCamera>(Sigil{}, deviceName, camInfo);
+    if (!c) {
+        LOG(ERROR) << "Failed to instantiate EvsMockCamera.";
+        return nullptr;
+    }
+
+    // Use the first resolution from the list for the testing
+    // TODO(b/214835237): Uses a given Stream configuration to choose the best
+    // stream configuration.
+    auto it = camInfo->streamConfigurations.begin();
+    c->mWidth = it->second.width;
+    c->mHeight = it->second.height;
+    c->mDescription.vendorFlags = 0xFFFFFFFF;  // Arbitrary test value
+
+    c->mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+    c->mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE |
+                GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY;
+
+    return c;
+}
+
+}  // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/src/GlWrapper.cpp b/automotive/evs/aidl/impl/default/src/GlWrapper.cpp
new file mode 100644
index 0000000..0ee5ecb
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/src/GlWrapper.cpp
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GlWrapper.h"
+
+#include <aidl/android/frameworks/automotive/display/DisplayDesc.h>
+#include <aidl/android/hardware/graphics/common/HardwareBufferDescription.h>
+#include <aidlcommonsupport/NativeHandle.h>
+#include <ui/DisplayMode.h>
+#include <ui/DisplayState.h>
+#include <ui/GraphicBuffer.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+
+#include <utility>
+
+namespace {
+
+using ::aidl::android::frameworks::automotive::display::DisplayDesc;
+using ::aidl::android::frameworks::automotive::display::ICarDisplayProxy;
+using ::aidl::android::frameworks::automotive::display::Rotation;
+using ::aidl::android::hardware::common::NativeHandle;
+using ::aidl::android::hardware::graphics::common::HardwareBufferDescription;
+using ::android::GraphicBuffer;
+using ::android::sp;
+
+constexpr const char vertexShaderSource[] =
+        "attribute vec4 pos;                  \n"
+        "attribute vec2 tex;                  \n"
+        "varying vec2 uv;                     \n"
+        "void main()                          \n"
+        "{                                    \n"
+        "   gl_Position = pos;                \n"
+        "   uv = tex;                         \n"
+        "}                                    \n";
+
+constexpr const char pixelShaderSource[] =
+        "precision mediump float;              \n"
+        "uniform sampler2D tex;                \n"
+        "varying vec2 uv;                      \n"
+        "void main()                           \n"
+        "{                                     \n"
+        "    gl_FragColor = texture2D(tex, uv);\n"
+        "}                                     \n";
+
+const char* getEGLError(void) {
+    switch (eglGetError()) {
+        case EGL_SUCCESS:
+            return "EGL_SUCCESS";
+        case EGL_NOT_INITIALIZED:
+            return "EGL_NOT_INITIALIZED";
+        case EGL_BAD_ACCESS:
+            return "EGL_BAD_ACCESS";
+        case EGL_BAD_ALLOC:
+            return "EGL_BAD_ALLOC";
+        case EGL_BAD_ATTRIBUTE:
+            return "EGL_BAD_ATTRIBUTE";
+        case EGL_BAD_CONTEXT:
+            return "EGL_BAD_CONTEXT";
+        case EGL_BAD_CONFIG:
+            return "EGL_BAD_CONFIG";
+        case EGL_BAD_CURRENT_SURFACE:
+            return "EGL_BAD_CURRENT_SURFACE";
+        case EGL_BAD_DISPLAY:
+            return "EGL_BAD_DISPLAY";
+        case EGL_BAD_SURFACE:
+            return "EGL_BAD_SURFACE";
+        case EGL_BAD_MATCH:
+            return "EGL_BAD_MATCH";
+        case EGL_BAD_PARAMETER:
+            return "EGL_BAD_PARAMETER";
+        case EGL_BAD_NATIVE_PIXMAP:
+            return "EGL_BAD_NATIVE_PIXMAP";
+        case EGL_BAD_NATIVE_WINDOW:
+            return "EGL_BAD_NATIVE_WINDOW";
+        case EGL_CONTEXT_LOST:
+            return "EGL_CONTEXT_LOST";
+        default:
+            return "Unknown error";
+    }
+}
+
+// Given shader source, load and compile it
+GLuint loadShader(GLenum type, const char* shaderSrc) {
+    // Create the shader object
+    GLuint shader = glCreateShader(type);
+    if (shader == 0) {
+        return 0;
+    }
+
+    // Load and compile the shader
+    glShaderSource(shader, 1, &shaderSrc, nullptr);
+    glCompileShader(shader);
+
+    // Verify the compilation worked as expected
+    GLint compiled = 0;
+    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+    if (!compiled) {
+        LOG(ERROR) << "Error compiling shader";
+
+        GLint size = 0;
+        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size);
+        if (size > 0) {
+            // Get and report the error message
+            char* infoLog = (char*)malloc(size);
+            glGetShaderInfoLog(shader, size, nullptr, infoLog);
+            LOG(ERROR) << "  msg:" << std::endl << infoLog;
+            free(infoLog);
+        }
+
+        glDeleteShader(shader);
+        return 0;
+    }
+
+    return shader;
+}
+
+// Create a program object given vertex and pixels shader source
+GLuint buildShaderProgram(const char* vtxSrc, const char* pxlSrc) {
+    GLuint program = glCreateProgram();
+    if (program == 0) {
+        LOG(ERROR) << "Failed to allocate program object";
+        return 0;
+    }
+
+    // Compile the shaders and bind them to this program
+    GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vtxSrc);
+    if (vertexShader == 0) {
+        LOG(ERROR) << "Failed to load vertex shader";
+        glDeleteProgram(program);
+        return 0;
+    }
+    GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pxlSrc);
+    if (pixelShader == 0) {
+        LOG(ERROR) << "Failed to load pixel shader";
+        glDeleteProgram(program);
+        glDeleteShader(vertexShader);
+        return 0;
+    }
+    glAttachShader(program, vertexShader);
+    glAttachShader(program, pixelShader);
+
+    glBindAttribLocation(program, 0, "pos");
+    glBindAttribLocation(program, 1, "tex");
+
+    // Link the program
+    glLinkProgram(program);
+    GLint linked = 0;
+    glGetProgramiv(program, GL_LINK_STATUS, &linked);
+    if (!linked) {
+        LOG(ERROR) << "Error linking program";
+        GLint size = 0;
+        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &size);
+        if (size > 0) {
+            // Get and report the error message
+            char* infoLog = (char*)malloc(size);
+            glGetProgramInfoLog(program, size, nullptr, infoLog);
+            LOG(ERROR) << "  msg:  " << infoLog;
+            free(infoLog);
+        }
+
+        glDeleteProgram(program);
+        glDeleteShader(vertexShader);
+        glDeleteShader(pixelShader);
+        return 0;
+    }
+
+    return program;
+}
+
+::android::sp<HGraphicBufferProducer> convertNativeHandleToHGBP(const NativeHandle& aidlHandle) {
+    native_handle_t* handle = ::android::dupFromAidl(aidlHandle);
+    if (handle->numFds != 0 || handle->numInts < std::ceil(sizeof(size_t) / sizeof(int))) {
+        LOG(ERROR) << "Invalid native handle";
+        return nullptr;
+    }
+    ::android::hardware::hidl_vec<uint8_t> halToken;
+    halToken.setToExternal(reinterpret_cast<uint8_t*>(const_cast<int*>(&(handle->data[1]))),
+                           handle->data[0]);
+    ::android::sp<HGraphicBufferProducer> hgbp =
+            HGraphicBufferProducer::castFrom(::android::retrieveHalInterface(halToken));
+    return std::move(hgbp);
+}
+
+}  // namespace
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+// Main entry point
+bool GlWrapper::initialize(const std::shared_ptr<ICarDisplayProxy>& pWindowProxy,
+                           uint64_t displayId) {
+    LOG(DEBUG) << __FUNCTION__;
+
+    if (!pWindowProxy) {
+        LOG(ERROR) << "Could not get ICarDisplayProxy.";
+        return false;
+    }
+
+    DisplayDesc displayDesc;
+    auto status = pWindowProxy->getDisplayInfo(displayId, &displayDesc);
+    if (!status.isOk()) {
+        LOG(ERROR) << "Failed to read the display information";
+        return false;
+    }
+
+    mWidth = displayDesc.width;
+    mHeight = displayDesc.height;
+    if ((displayDesc.orientation != Rotation::ROTATION_0) &&
+        (displayDesc.orientation != Rotation::ROTATION_180)) {
+        std::swap(mWidth, mHeight);
+    }
+    LOG(INFO) << "Display resolution is " << mWidth << "x" << mHeight;
+
+    NativeHandle aidlHandle;
+    status = pWindowProxy->getHGraphicBufferProducer(displayId, &aidlHandle);
+    if (!status.isOk()) {
+        LOG(ERROR) << "Failed to get IGraphicBufferProducer from ICarDisplayProxy.";
+        return false;
+    }
+
+    mGfxBufferProducer = convertNativeHandleToHGBP(aidlHandle);
+    if (!mGfxBufferProducer) {
+        LOG(ERROR) << "Failed to convert a NativeHandle to HGBP.";
+        return false;
+    }
+
+    mSurfaceHolder = getSurfaceFromHGBP(mGfxBufferProducer);
+    if (mSurfaceHolder == nullptr) {
+        LOG(ERROR) << "Failed to get a Surface from HGBP.";
+        return false;
+    }
+
+    mWindow = getNativeWindow(mSurfaceHolder.get());
+    if (mWindow == nullptr) {
+        LOG(ERROR) << "Failed to get a native window from Surface.";
+        return false;
+    }
+
+    // Set up our OpenGL ES context associated with the default display
+    mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    if (mDisplay == EGL_NO_DISPLAY) {
+        LOG(ERROR) << "Failed to get egl display";
+        return false;
+    }
+
+    EGLint major = 2;
+    EGLint minor = 0;
+    if (!eglInitialize(mDisplay, &major, &minor)) {
+        LOG(ERROR) << "Failed to initialize EGL: " << getEGLError();
+        return false;
+    }
+
+    const EGLint config_attribs[] = {
+            // clang-format off
+            // Tag          Value
+            EGL_RED_SIZE,   8,
+            EGL_GREEN_SIZE, 8,
+            EGL_BLUE_SIZE,  8,
+            EGL_DEPTH_SIZE, 0,
+            EGL_NONE
+            // clang-format on
+    };
+
+    // Pick the default configuration without constraints (is this good enough?)
+    EGLConfig egl_config = {0};
+    EGLint numConfigs = -1;
+    eglChooseConfig(mDisplay, config_attribs, &egl_config, 1, &numConfigs);
+    if (numConfigs != 1) {
+        LOG(ERROR) << "Didn't find a suitable format for our display window, " << getEGLError();
+        return false;
+    }
+
+    // Create the EGL render target surface
+    mSurface = eglCreateWindowSurface(mDisplay, egl_config, mWindow, nullptr);
+    if (mSurface == EGL_NO_SURFACE) {
+        LOG(ERROR) << "eglCreateWindowSurface failed, " << getEGLError();
+        return false;
+    }
+
+    // Create the EGL context
+    // NOTE:  Our shader is (currently at least) written to require version 3, so this
+    //        is required.
+    const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
+    mContext = eglCreateContext(mDisplay, egl_config, EGL_NO_CONTEXT, context_attribs);
+    if (mContext == EGL_NO_CONTEXT) {
+        LOG(ERROR) << "Failed to create OpenGL ES Context: " << getEGLError();
+        return false;
+    }
+
+    // Activate our render target for drawing
+    if (!eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)) {
+        LOG(ERROR) << "Failed to make the OpenGL ES Context current: " << getEGLError();
+        return false;
+    }
+
+    // Create the shader program for our simple pipeline
+    mShaderProgram = buildShaderProgram(vertexShaderSource, pixelShaderSource);
+    if (!mShaderProgram) {
+        LOG(ERROR) << "Failed to build shader program: " << getEGLError();
+        return false;
+    }
+
+    // Create a GL texture that will eventually wrap our externally created texture surface(s)
+    glGenTextures(1, &mTextureMap);
+    if (mTextureMap <= 0) {
+        LOG(ERROR) << "Didn't get a texture handle allocated: " << getEGLError();
+        return false;
+    }
+
+    // Turn off mip-mapping for the created texture surface
+    // (the inbound camera imagery doesn't have MIPs)
+    glBindTexture(GL_TEXTURE_2D, mTextureMap);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glBindTexture(GL_TEXTURE_2D, 0);
+
+    return true;
+}
+
+void GlWrapper::shutdown() {
+    // Drop our device textures
+    if (mKHRimage != EGL_NO_IMAGE_KHR) {
+        eglDestroyImageKHR(mDisplay, mKHRimage);
+        mKHRimage = EGL_NO_IMAGE_KHR;
+    }
+
+    // Release all GL resources
+    if (eglGetCurrentContext() == mContext) {
+        eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    }
+    eglDestroySurface(mDisplay, mSurface);
+    eglDestroyContext(mDisplay, mContext);
+    eglTerminate(mDisplay);
+    mSurface = EGL_NO_SURFACE;
+    mContext = EGL_NO_CONTEXT;
+    mDisplay = EGL_NO_DISPLAY;
+
+    // Release the window
+    mSurfaceHolder = nullptr;
+}
+
+void GlWrapper::showWindow(const std::shared_ptr<ICarDisplayProxy>& pWindowProxy, uint64_t id) {
+    if (pWindowProxy) {
+        pWindowProxy->showWindow(id);
+    } else {
+        LOG(ERROR) << "ICarDisplayProxy is not available.";
+    }
+}
+
+void GlWrapper::hideWindow(const std::shared_ptr<ICarDisplayProxy>& pWindowProxy, uint64_t id) {
+    if (pWindowProxy) {
+        pWindowProxy->hideWindow(id);
+    } else {
+        LOG(ERROR) << "ICarDisplayProxy is not available.";
+    }
+}
+
+bool GlWrapper::updateImageTexture(buffer_handle_t handle,
+                                   const HardwareBufferDescription& description) {
+    if (mKHRimage != EGL_NO_IMAGE_KHR) {
+        return true;
+    }
+
+    // Create a temporary GraphicBuffer to wrap the provided handle.
+    sp<GraphicBuffer> pGfxBuffer =
+            new GraphicBuffer(description.width, description.height,
+                              static_cast<::android::PixelFormat>(description.format),
+                              description.layers, static_cast<uint32_t>(description.usage),
+                              description.stride, const_cast<native_handle_t*>(handle),
+                              /* keepOwnership= */ false);
+    if (!pGfxBuffer) {
+        LOG(ERROR) << "Failed to allocate GraphicBuffer to wrap our native handle";
+        return false;
+    }
+
+    // Get a GL compatible reference to the graphics buffer we've been given
+    EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
+    EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
+    mKHRimage = eglCreateImageKHR(mDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf,
+                                  eglImageAttributes);
+    if (mKHRimage == EGL_NO_IMAGE_KHR) {
+        LOG(ERROR) << "Error creating EGLImage: " << getEGLError();
+        return false;
+    }
+
+    // Update the texture handle we already created to refer to this gralloc buffer
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, mTextureMap);
+    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(mKHRimage));
+
+    return true;
+}
+
+void GlWrapper::renderImageToScreen() {
+    // Set the viewport
+    glViewport(0, 0, mWidth, mHeight);
+
+    // Clear the color buffer
+    glClearColor(0.1f, 0.5f, 0.1f, 1.0f);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    // Select our screen space simple texture shader
+    glUseProgram(mShaderProgram);
+
+    // Bind the texture and assign it to the shader's sampler
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, mTextureMap);
+    GLint sampler = glGetUniformLocation(mShaderProgram, "tex");
+    glUniform1i(sampler, 0);
+
+    // We want our image to show up opaque regardless of alpha values
+    glDisable(GL_BLEND);
+
+    // Draw a rectangle on the screen
+    GLfloat vertsCarPos[] = {
+            // clang-format off
+            -0.8,  0.8, 0.0f,  // left top in window space
+             0.8,  0.8, 0.0f,  // right top
+            -0.8, -0.8, 0.0f,  // left bottom
+             0.8, -0.8, 0.0f   // right bottom
+            // clang-format on
+    };
+
+    // NOTE:  We didn't flip the image in the texture, so V=0 is actually the top of the image
+    GLfloat vertsCarTex[] = {
+            // clang-format off
+            0.0f, 0.0f,  // left top
+            1.0f, 0.0f,  // right top
+            0.0f, 1.0f,  // left bottom
+            1.0f, 1.0f   // right bottom
+            // clang-format on
+    };
+    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsCarPos);
+    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertsCarTex);
+    glEnableVertexAttribArray(0);
+    glEnableVertexAttribArray(1);
+
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+    // Clean up and flip the rendered result to the front so it is visible
+    glDisableVertexAttribArray(0);
+    glDisableVertexAttribArray(1);
+
+    glFinish();
+
+    if (eglSwapBuffers(mDisplay, mSurface) == EGL_FALSE) {
+        LOG(WARNING) << "Failed to swap EGL buffers, " << getEGLError();
+    }
+}
+
+}  // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/src/service.cpp b/automotive/evs/aidl/impl/default/src/service.cpp
index 0a0913f..7532d87 100644
--- a/automotive/evs/aidl/impl/default/src/service.cpp
+++ b/automotive/evs/aidl/impl/default/src/service.cpp
@@ -14,38 +14,75 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "EvsService"
-
-#include <DefaultEvsEnumerator.h>
+#include "EvsEnumerator.h"
+#include "EvsGlDisplay.h"
 
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
 #include <utils/Log.h>
 
-using ::aidl::android::hardware::automotive::evs::implementation::DefaultEvsEnumerator;
+#include <unistd.h>
 
-int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {
-    std::shared_ptr<DefaultEvsEnumerator> vhal = ndk::SharedRefBase::make<DefaultEvsEnumerator>();
+#include <atomic>
+#include <cstdlib>
+#include <string_view>
 
-    ALOGI("Registering as service...");
-    binder_exception_t err =
-            AServiceManager_addService(vhal->asBinder().get(), "android.hardware.automotive.evs");
+namespace {
+
+using ::aidl::android::frameworks::automotive::display::ICarDisplayProxy;
+using ::aidl::android::hardware::automotive::evs::implementation::EvsEnumerator;
+
+constexpr std::string_view kDisplayServiceInstanceName = "/default";
+constexpr std::string_view kHwInstanceName = "/hw/0";
+constexpr int kNumBinderThreads = 1;
+
+}  // namespace
+
+int main() {
+    LOG(INFO) << "EVS Hardware Enumerator service is starting";
+
+    const std::string displayServiceInstanceName =
+            std::string(ICarDisplayProxy::descriptor) + std::string(kDisplayServiceInstanceName);
+    if (!AServiceManager_isDeclared(displayServiceInstanceName.data())) {
+        // TODO: We may just want to disable EVS display.
+        LOG(ERROR) << displayServiceInstanceName << " is required.";
+        return EXIT_FAILURE;
+    }
+
+    std::shared_ptr<ICarDisplayProxy> displayService = ICarDisplayProxy::fromBinder(
+            ::ndk::SpAIBinder(AServiceManager_waitForService(displayServiceInstanceName.data())));
+    if (!displayService) {
+        LOG(ERROR) << "Cannot use " << displayServiceInstanceName << ".  Exiting.";
+        return EXIT_FAILURE;
+    }
+
+    // Register our service -- if somebody is already registered by our name,
+    // they will be killed (their thread pool will throw an exception).
+    std::shared_ptr<EvsEnumerator> service =
+            ndk::SharedRefBase::make<EvsEnumerator>(displayService);
+    if (!service) {
+        LOG(ERROR) << "Failed to instantiate the service";
+        return EXIT_FAILURE;
+    }
+
+    const std::string instanceName =
+            std::string(EvsEnumerator::descriptor) + std::string(kHwInstanceName);
+    auto err = AServiceManager_addService(service->asBinder().get(), instanceName.data());
     if (err != EX_NONE) {
-        ALOGE("failed to register android.hardware.automotive.evs service, exception: %d", err);
-        return 1;
+        LOG(ERROR) << "Failed to register " << instanceName << ", exception = " << err;
+        return EXIT_FAILURE;
     }
 
-    if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) {
-        ALOGE("%s", "failed to set thread pool max thread count");
-        return 1;
+    if (!ABinderProcess_setThreadPoolMaxThreadCount(kNumBinderThreads)) {
+        LOG(ERROR) << "Failed to set thread pool";
+        return EXIT_FAILURE;
     }
+
     ABinderProcess_startThreadPool();
-
-    ALOGI("Evs Service Ready");
+    LOG(INFO) << "EVS Hardware Enumerator is ready";
 
     ABinderProcess_joinThreadPool();
-
-    ALOGI("Evs Service Exiting");
-
-    return 0;
+    // In normal operation, we don't expect the thread pool to exit
+    LOG(INFO) << "EVS Hardware Enumerator is shutting down";
+    return EXIT_SUCCESS;
 }
diff --git a/automotive/evs/aidl/vts/Android.bp b/automotive/evs/aidl/vts/Android.bp
index 5aa9501..e50c913 100644
--- a/automotive/evs/aidl/vts/Android.bp
+++ b/automotive/evs/aidl/vts/Android.bp
@@ -14,18 +14,17 @@
 // limitations under the License.
 //
 
-package{
+package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
     // all of the 'license_kinds' from "hardware_interfaces_license"
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses : ["hardware_interfaces_license"],
+    default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
 cc_test {
-name:
-    "VtsHalEvsTargetTest",
+    name: "VtsHalEvsTargetTest",
     srcs: [
         "*.cpp",
     ],
@@ -42,7 +41,7 @@
     ],
     static_libs: [
         "android.hardware.automotive.evs@common-default-lib",
-        "android.hardware.automotive.evs-V1-ndk",
+        "android.hardware.automotive.evs-V2-ndk",
         "android.hardware.common-V2-ndk",
         "libaidlcommonsupport",
     ],
diff --git a/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp b/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp
index 2706c49..a6d99ad 100644
--- a/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp
+++ b/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp
@@ -51,6 +51,7 @@
 #include <ui/GraphicBufferAllocator.h>
 #include <utils/Timers.h>
 
+#include <chrono>
 #include <deque>
 #include <thread>
 #include <unordered_set>
@@ -2197,6 +2198,203 @@
     }
 }
 
+/*
+ * DisplayOpen:
+ * Test both clean shut down and "aggressive open" device stealing behavior.
+ */
+TEST_P(EvsAidlTest, DisplayOpen) {
+    LOG(INFO) << "Starting DisplayOpen test";
+
+    // Request available display IDs.
+    std::vector<uint8_t> displayIds;
+    ASSERT_TRUE(mEnumerator->getDisplayIdList(&displayIds).isOk());
+    EXPECT_GT(displayIds.size(), 0);
+
+    for (const auto displayId : displayIds) {
+        std::shared_ptr<IEvsDisplay> pDisplay;
+
+        // Request exclusive access to each EVS display, then let it go.
+        ASSERT_TRUE(mEnumerator->openDisplay(displayId, &pDisplay).isOk());
+        ASSERT_NE(pDisplay, nullptr);
+
+        {
+            // Ask the display what its name is.
+            DisplayDesc desc;
+            ASSERT_TRUE(pDisplay->getDisplayInfo(&desc).isOk());
+            LOG(DEBUG) << "Found display " << desc.id;
+        }
+
+        ASSERT_TRUE(mEnumerator->closeDisplay(pDisplay).isOk());
+
+        // Ensure we can reopen the display after it has been closed.
+        ASSERT_TRUE(mEnumerator->openDisplay(displayId, &pDisplay).isOk());
+        ASSERT_NE(pDisplay, nullptr);
+
+        // Open the display while its already open -- ownership should be transferred.
+        std::shared_ptr<IEvsDisplay> pDisplay2;
+        ASSERT_TRUE(mEnumerator->openDisplay(displayId, &pDisplay2).isOk());
+        ASSERT_NE(pDisplay2, nullptr);
+
+        {
+            // Ensure the old display properly reports its assassination.
+            DisplayState badState;
+            EXPECT_TRUE(pDisplay->getDisplayState(&badState).isOk());
+            EXPECT_EQ(badState, DisplayState::DEAD);
+        }
+
+        // Close only the newest display instance -- the other should already be a zombie.
+        ASSERT_TRUE(mEnumerator->closeDisplay(pDisplay2).isOk());
+
+        // Finally, validate that we can open the display after the provoked failure above.
+        ASSERT_TRUE(mEnumerator->openDisplay(displayId, &pDisplay).isOk());
+        ASSERT_NE(pDisplay, nullptr);
+        ASSERT_TRUE(mEnumerator->closeDisplay(pDisplay).isOk());
+    }
+}
+
+/*
+ * DisplayStates:
+ * Validate that display states transition as expected and can be queried from either the display
+ * object itself or the owning enumerator.
+ */
+TEST_P(EvsAidlTest, DisplayStates) {
+    using std::literals::chrono_literals::operator""ms;
+
+    LOG(INFO) << "Starting DisplayStates test";
+
+    // Request available display IDs.
+    std::vector<uint8_t> displayIds;
+    ASSERT_TRUE(mEnumerator->getDisplayIdList(&displayIds).isOk());
+    EXPECT_GT(displayIds.size(), 0);
+
+    for (const auto displayId : displayIds) {
+        // Ensure the display starts in the expected state.
+        {
+            DisplayState state;
+            EXPECT_FALSE(mEnumerator->getDisplayState(&state).isOk());
+        }
+        for (const auto displayIdToQuery : displayIds) {
+            DisplayState state;
+            EXPECT_FALSE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+        }
+
+        // Scope to limit the lifetime of the pDisplay pointer, and thus the IEvsDisplay object.
+        {
+            // Request exclusive access to the EVS display.
+            std::shared_ptr<IEvsDisplay> pDisplay;
+            ASSERT_TRUE(mEnumerator->openDisplay(displayId, &pDisplay).isOk());
+            ASSERT_NE(pDisplay, nullptr);
+            {
+                DisplayState state;
+                EXPECT_TRUE(mEnumerator->getDisplayState(&state).isOk());
+                EXPECT_EQ(state, DisplayState::NOT_VISIBLE);
+            }
+            for (const auto displayIdToQuery : displayIds) {
+                DisplayState state;
+                if (displayIdToQuery == displayId) {
+                    EXPECT_TRUE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+                    EXPECT_EQ(state, DisplayState::NOT_VISIBLE);
+                } else {
+                    EXPECT_FALSE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+                }
+            }
+
+            // Activate the display.
+            EXPECT_TRUE(pDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME).isOk());
+            {
+                DisplayState state;
+                EXPECT_TRUE(mEnumerator->getDisplayState(&state).isOk());
+                EXPECT_EQ(state, DisplayState::VISIBLE_ON_NEXT_FRAME);
+            }
+            {
+                DisplayState state;
+                EXPECT_TRUE(pDisplay->getDisplayState(&state).isOk());
+                EXPECT_EQ(state, DisplayState::VISIBLE_ON_NEXT_FRAME);
+            }
+            for (const auto displayIdToQuery : displayIds) {
+                DisplayState state;
+                if (displayIdToQuery == displayId) {
+                    EXPECT_TRUE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+                    EXPECT_EQ(state, DisplayState::VISIBLE_ON_NEXT_FRAME);
+                } else {
+                    EXPECT_FALSE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+                }
+            }
+
+            // Get the output buffer we'd use to display the imagery.
+            BufferDesc tgtBuffer;
+            ASSERT_TRUE(pDisplay->getTargetBuffer(&tgtBuffer).isOk());
+
+            // Send the target buffer back for display (we didn't actually fill anything).
+            EXPECT_TRUE(pDisplay->returnTargetBufferForDisplay(tgtBuffer).isOk());
+
+            // Sleep for a tenth of a second to ensure the driver has time to get the image
+            // displayed.
+            std::this_thread::sleep_for(100ms);
+            {
+                DisplayState state;
+                EXPECT_TRUE(mEnumerator->getDisplayState(&state).isOk());
+                EXPECT_EQ(state, DisplayState::VISIBLE);
+            }
+            {
+                DisplayState state;
+                EXPECT_TRUE(pDisplay->getDisplayState(&state).isOk());
+                EXPECT_EQ(state, DisplayState::VISIBLE);
+            }
+            for (const auto displayIdToQuery : displayIds) {
+                DisplayState state;
+                if (displayIdToQuery == displayId) {
+                    EXPECT_TRUE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+                    EXPECT_EQ(state, DisplayState::VISIBLE);
+                } else {
+                    EXPECT_FALSE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+                }
+            }
+
+            // Turn off the display.
+            EXPECT_TRUE(pDisplay->setDisplayState(DisplayState::NOT_VISIBLE).isOk());
+            std::this_thread::sleep_for(100ms);
+            {
+                DisplayState state;
+                EXPECT_TRUE(mEnumerator->getDisplayState(&state).isOk());
+                EXPECT_EQ(state, DisplayState::NOT_VISIBLE);
+            }
+            {
+                DisplayState state;
+                EXPECT_TRUE(pDisplay->getDisplayState(&state).isOk());
+                EXPECT_EQ(state, DisplayState::NOT_VISIBLE);
+            }
+            for (const auto displayIdToQuery : displayIds) {
+                DisplayState state;
+                if (displayIdToQuery == displayId) {
+                    EXPECT_TRUE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+                    EXPECT_EQ(state, DisplayState::NOT_VISIBLE);
+                } else {
+                    EXPECT_FALSE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+                }
+            }
+
+            // Close the display.
+            mEnumerator->closeDisplay(pDisplay);
+        }
+
+        // Now that the display pointer has gone out of scope, causing the IEvsDisplay interface
+        // object to be destroyed, we should be back to the "not open" state.
+        // NOTE:  If we want this to pass without the sleep above, we'd have to add the
+        //        (now recommended) closeDisplay() call instead of relying on the smarter pointer
+        //        going out of scope.  I've not done that because I want to verify that the deletion
+        //        of the object does actually clean up (eventually).
+        {
+            DisplayState state;
+            EXPECT_FALSE(mEnumerator->getDisplayState(&state).isOk());
+        }
+        for (const auto displayIdToQuery : displayIds) {
+            DisplayState state;
+            EXPECT_FALSE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+        }
+    }
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EvsAidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, EvsAidlTest,
diff --git a/automotive/occupant_awareness/aidl/default/Android.bp b/automotive/occupant_awareness/aidl/default/Android.bp
index 1b760a5..dc280df 100644
--- a/automotive/occupant_awareness/aidl/default/Android.bp
+++ b/automotive/occupant_awareness/aidl/default/Android.bp
@@ -26,6 +26,7 @@
 cc_binary {
     name: "android.hardware.automotive.occupant_awareness@1.0-service",
     init_rc: ["android.hardware.automotive.occupant_awareness@1.0-service.rc"],
+    vintf_fragments: ["android.hardware.automotive.occupant_awareness-service.xml"],
     relative_install_path: "hw",
     vendor: true,
     srcs: [
diff --git a/automotive/occupant_awareness/aidl/default/android.hardware.automotive.occupant_awareness-service.xml b/automotive/occupant_awareness/aidl/default/android.hardware.automotive.occupant_awareness-service.xml
new file mode 100644
index 0000000..b4f8fa5
--- /dev/null
+++ b/automotive/occupant_awareness/aidl/default/android.hardware.automotive.occupant_awareness-service.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.automotive.occupant_awareness</name>
+        <version>1</version>
+        <fqname>IOccupantAwareness/default</fqname>
+    </hal>
+</manifest>
diff --git a/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
index 9b6eb2f..b0935c2 100644
--- a/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
+++ b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
@@ -34,8 +34,9 @@
 package android.hardware.automotive.remoteaccess;
 @VintfStability
 interface IRemoteAccess {
-  String getDeviceId();
+  String getVehicleId();
   String getWakeupServiceName();
+  String getProcessorId();
   void setRemoteTaskCallback(android.hardware.automotive.remoteaccess.IRemoteTaskCallback callback);
   void clearRemoteTaskCallback();
   void notifyApStateChange(in android.hardware.automotive.remoteaccess.ApState state);
diff --git a/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
index a198b03..0f4125f 100644
--- a/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
+++ b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
@@ -28,19 +28,19 @@
 @VintfStability
 interface IRemoteAccess {
     /**
-     * Gets a unique device ID that could be recognized by wake up server.
+     * Gets a unique vehicle ID that could be recognized by wake up server.
      *
-     * This device ID is provisioned during car production and is registered
+     * <p>This vehicle ID is provisioned during car production and is registered
      * with the wake up server.
      *
-     * @return a unique device ID.
+     * @return a unique vehicle ID.
      */
-    String getDeviceId();
+    String getVehicleId();
 
     /**
      * Gets the name for the remote wakeup server.
      *
-     * This name will be provided to remote task server during registration
+     * <p>This name will be provided to remote task server during registration
      * and used by remote task server to find the remote wakeup server to
      * use for waking up the device. This name must be pre-negotiated between
      * the remote wakeup server/client and the remote task server/client and
@@ -51,6 +51,17 @@
     String getWakeupServiceName();
 
     /**
+     * Gets a unique processor ID that could be recognized by wake up client.
+     *
+     * <p>This processor ID is used to identify each processor in the vehicle.
+     * The wake up client which handles many processors determines which
+     * processor to wake up from the processor ID.
+     *
+     * <p> The processor ID must be unique in the vehicle.
+     */
+    String getProcessorId();
+
+    /**
      * Sets a callback to be called when a remote task is requested.
      *
      * @param callback A callback to be called when a remote task is requested.
diff --git a/automotive/remoteaccess/hal/default/include/RemoteAccessService.h b/automotive/remoteaccess/hal/default/include/RemoteAccessService.h
index 74c2af4..9aabad6 100644
--- a/automotive/remoteaccess/hal/default/include/RemoteAccessService.h
+++ b/automotive/remoteaccess/hal/default/include/RemoteAccessService.h
@@ -62,7 +62,9 @@
 
     ~RemoteAccessService();
 
-    ndk::ScopedAStatus getDeviceId(std::string* deviceId) override;
+    ndk::ScopedAStatus getVehicleId(std::string* vehicleId) override;
+
+    ndk::ScopedAStatus getProcessorId(std::string* processorId) override;
 
     ndk::ScopedAStatus getWakeupServiceName(std::string* wakeupServiceName) override;
 
@@ -95,7 +97,10 @@
     bool mTaskWaitStopped GUARDED_BY(mLock);
     // A mutex to make sure startTaskLoop does not overlap with stopTaskLoop.
     std::mutex mStartStopTaskLoopLock;
-    bool mTaskLoopRunning GUARDED_BY(mStartStopTaskLoopLock);
+    bool mTaskLoopRunning GUARDED_BY(mStartStopTaskLoopLock) = false;
+    bool mGrpcConnected GUARDED_BY(mLock) = false;
+    std::unordered_map<std::string, size_t> mClientIdToTaskCount GUARDED_BY(mLock);
+
     // Default wait time before retry connecting to remote access client is 10s.
     size_t mRetryWaitInMs = 10'000;
     std::shared_ptr<DebugRemoteTaskCallback> mDebugCallback;
@@ -103,11 +108,17 @@
     void runTaskLoop();
     void maybeStartTaskLoop();
     void maybeStopTaskLoop();
-    ndk::ScopedAStatus getDeviceIdWithClient(
-            android::frameworks::automotive::vhal::IVhalClient& client, std::string* deviceId);
+    ndk::ScopedAStatus getVehicleIdWithClient(
+            android::frameworks::automotive::vhal::IVhalClient& client, std::string* vehicleId);
 
     void setRetryWaitInMs(size_t retryWaitInMs) { mRetryWaitInMs = retryWaitInMs; }
     void dumpHelp(int fd);
+    void printCurrentStatus(int fd);
+    std::string clientIdToTaskCountToStringLocked() REQUIRES(mLock);
+    void debugInjectTask(int fd, std::string_view clientId, std::string_view taskData);
+    void updateGrpcConnected(bool connected);
+    android::base::Result<void> deliverRemoteTaskThroughCallback(const std::string& clientId,
+                                                                 std::string_view taskData);
 };
 
 }  // namespace remoteaccess
diff --git a/automotive/remoteaccess/hal/default/remoteaccess-tcu-test-service.rc b/automotive/remoteaccess/hal/default/remoteaccess-tcu-test-service.rc
index 6437d70..59315eb 100644
--- a/automotive/remoteaccess/hal/default/remoteaccess-tcu-test-service.rc
+++ b/automotive/remoteaccess/hal/default/remoteaccess-tcu-test-service.rc
@@ -2,3 +2,4 @@
     class hal
     user vehicle_network
     group system inet
+    capabilities NET_RAW
diff --git a/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
index 4be30a2..bbda9df 100644
--- a/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
+++ b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
@@ -36,6 +36,8 @@
 using ::aidl::android::hardware::automotive::remoteaccess::ApState;
 using ::aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback;
 using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
+using ::android::base::Error;
+using ::android::base::Result;
 using ::android::base::ScopedLockAssertion;
 using ::android::base::StringAppendF;
 using ::android::base::StringPrintf;
@@ -48,13 +50,16 @@
 using ::ndk::ScopedAStatus;
 
 const std::string WAKEUP_SERVICE_NAME = "com.google.vehicle.wakeup";
+const std::string PROCESSOR_ID = "application_processor";
 constexpr char COMMAND_SET_AP_STATE[] = "--set-ap-state";
 constexpr char COMMAND_START_DEBUG_CALLBACK[] = "--start-debug-callback";
 constexpr char COMMAND_STOP_DEBUG_CALLBACK[] = "--stop-debug-callback";
 constexpr char COMMAND_SHOW_TASK[] = "--show-task";
-constexpr char COMMAND_GET_DEVICE_ID[] = "--get-device-id";
+constexpr char COMMAND_GET_VEHICLE_ID[] = "--get-vehicle-id";
+constexpr char COMMAND_INJECT_TASK[] = "--inject-task";
+constexpr char COMMAND_STATUS[] = "--status";
 
-std::vector<uint8_t> stringToBytes(const std::string& s) {
+std::vector<uint8_t> stringToBytes(std::string_view s) {
     const char* data = s.data();
     return std::vector<uint8_t>(data, data + s.size());
 }
@@ -80,6 +85,10 @@
     dprintf(fd, "%s, code: %d, error: %s\n", detail, status.getStatus(), status.getMessage());
 }
 
+std::string boolToString(bool x) {
+    return x ? "true" : "false";
+}
+
 }  // namespace
 
 RemoteAccessService::RemoteAccessService(WakeupClient::StubInterface* grpcStub)
@@ -125,6 +134,33 @@
     mTaskLoopRunning = false;
 }
 
+void RemoteAccessService::updateGrpcConnected(bool connected) {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+    mGrpcConnected = connected;
+}
+
+Result<void> RemoteAccessService::deliverRemoteTaskThroughCallback(const std::string& clientId,
+                                                                   std::string_view taskData) {
+    std::shared_ptr<IRemoteTaskCallback> callback;
+    {
+        std::lock_guard<std::mutex> lockGuard(mLock);
+        callback = mRemoteTaskCallback;
+        mClientIdToTaskCount[clientId] += 1;
+    }
+    if (callback == nullptr) {
+        return Error() << "No callback registered, task ignored";
+    }
+    ALOGD("Calling onRemoteTaskRequested callback for client ID: %s", clientId.c_str());
+    ScopedAStatus callbackStatus =
+            callback->onRemoteTaskRequested(clientId, stringToBytes(taskData));
+    if (!callbackStatus.isOk()) {
+        return Error() << "Failed to call onRemoteTaskRequested callback, status: "
+                       << callbackStatus.getStatus()
+                       << ", message: " << callbackStatus.getMessage();
+    }
+    return {};
+}
+
 void RemoteAccessService::runTaskLoop() {
     GetRemoteTasksRequest request = {};
     std::unique_ptr<ClientReaderInterface<GetRemoteTasksResponse>> reader;
@@ -134,28 +170,19 @@
             mGetRemoteTasksContext.reset(new ClientContext());
             reader = mGrpcStub->GetRemoteTasks(mGetRemoteTasksContext.get(), request);
         }
+        updateGrpcConnected(true);
         GetRemoteTasksResponse response;
         while (reader->Read(&response)) {
             ALOGI("Receiving one task from remote task client");
 
-            std::shared_ptr<IRemoteTaskCallback> callback;
-            {
-                std::lock_guard<std::mutex> lockGuard(mLock);
-                callback = mRemoteTaskCallback;
-            }
-            if (callback == nullptr) {
-                ALOGD("No callback registered, task ignored");
+            if (auto result =
+                        deliverRemoteTaskThroughCallback(response.clientid(), response.data());
+                !result.ok()) {
+                ALOGE("%s", result.error().message().c_str());
                 continue;
             }
-            ALOGD("Calling onRemoteTaskRequested callback for client ID: %s",
-                  response.clientid().c_str());
-            ScopedAStatus callbackStatus = callback->onRemoteTaskRequested(
-                    response.clientid(), stringToBytes(response.data()));
-            if (!callbackStatus.isOk()) {
-                ALOGE("Failed to call onRemoteTaskRequested callback, status: %d, message: %s",
-                      callbackStatus.getStatus(), callbackStatus.getMessage());
-            }
         }
+        updateGrpcConnected(false);
         Status status = reader->Finish();
         mGetRemoteTasksContext.reset();
 
@@ -176,23 +203,23 @@
     }
 }
 
-ScopedAStatus RemoteAccessService::getDeviceId(std::string* deviceId) {
+ScopedAStatus RemoteAccessService::getVehicleId(std::string* vehicleId) {
 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
     auto vhalClient = IVhalClient::tryCreate();
     if (vhalClient == nullptr) {
         ALOGE("Failed to connect to VHAL");
         return ScopedAStatus::fromServiceSpecificErrorWithMessage(
-                /*errorCode=*/0, "Failed to connect to VHAL to get device ID");
+                /*errorCode=*/0, "Failed to connect to VHAL to get vehicle ID");
     }
-    return getDeviceIdWithClient(*vhalClient.get(), deviceId);
+    return getVehicleIdWithClient(*vhalClient.get(), vehicleId);
 #else
     // Don't use VHAL client in fuzzing since IPC is not allowed.
     return ScopedAStatus::ok();
 #endif
 }
 
-ScopedAStatus RemoteAccessService::getDeviceIdWithClient(IVhalClient& vhalClient,
-                                                         std::string* deviceId) {
+ScopedAStatus RemoteAccessService::getVehicleIdWithClient(IVhalClient& vhalClient,
+                                                          std::string* vehicleId) {
     auto result = vhalClient.getValueSync(
             *vhalClient.createHalPropValue(toInt(VehicleProperty::INFO_VIN)));
     if (!result.ok()) {
@@ -200,7 +227,12 @@
                 /*errorCode=*/0,
                 ("failed to get INFO_VIN from VHAL: " + result.error().message()).c_str());
     }
-    *deviceId = (*result)->getStringValue();
+    *vehicleId = (*result)->getStringValue();
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteAccessService::getProcessorId(std::string* processorId) {
+    *processorId = PROCESSOR_ID;
     return ScopedAStatus::ok();
 }
 
@@ -246,15 +278,17 @@
 }
 
 void RemoteAccessService::dumpHelp(int fd) {
-    dprintf(fd, "%s",
-            (std::string("RemoteAccess HAL debug interface, Usage: \n") + COMMAND_SET_AP_STATE +
-             " [0/1](isReadyForRemoteTask) [0/1](isWakeupRequired)  Set the new AP state\n" +
-             COMMAND_START_DEBUG_CALLBACK +
-             " Start a debug callback that will record the received tasks\n" +
-             COMMAND_STOP_DEBUG_CALLBACK + " Stop the debug callback\n" + COMMAND_SHOW_TASK +
-             " Show tasks received by debug callback\n" + COMMAND_GET_DEVICE_ID +
-             " Get device id\n")
-                    .c_str());
+    dprintf(fd,
+            "RemoteAccess HAL debug interface, Usage: \n"
+            "%s [0/1](isReadyForRemoteTask) [0/1](isWakeupRequired): Set the new AP state\n"
+            "%s: Start a debug callback that will record the received tasks\n"
+            "%s: Stop the debug callback\n"
+            "%s: Show tasks received by debug callback\n"
+            "%s: Get vehicle id\n"
+            "%s [client_id] [task_data]: Inject a task\n"
+            "%s: Show status\n",
+            COMMAND_SET_AP_STATE, COMMAND_START_DEBUG_CALLBACK, COMMAND_STOP_DEBUG_CALLBACK,
+            COMMAND_SHOW_TASK, COMMAND_GET_VEHICLE_ID, COMMAND_INJECT_TASK, COMMAND_STATUS);
 }
 
 binder_status_t RemoteAccessService::dump(int fd, const char** args, uint32_t numArgs) {
@@ -265,6 +299,7 @@
 
     if (numArgs == 0) {
         dumpHelp(fd);
+        printCurrentStatus(fd);
         return STATUS_OK;
     }
 
@@ -316,14 +351,22 @@
             dprintf(fd, "Debug callback is not currently used, use \"%s\" first.\n",
                     COMMAND_START_DEBUG_CALLBACK);
         }
-    } else if (!strcmp(args[0], COMMAND_GET_DEVICE_ID)) {
-        std::string deviceId;
-        auto status = getDeviceId(&deviceId);
+    } else if (!strcmp(args[0], COMMAND_GET_VEHICLE_ID)) {
+        std::string vehicleId;
+        auto status = getVehicleId(&vehicleId);
         if (!status.isOk()) {
-            dprintErrorStatus(fd, "Failed to get device ID", status);
+            dprintErrorStatus(fd, "Failed to get vehicle ID", status);
         } else {
-            dprintf(fd, "Device Id: %s\n", deviceId.c_str());
+            dprintf(fd, "Vehicle Id: %s\n", vehicleId.c_str());
         }
+    } else if (!strcmp(args[0], COMMAND_INJECT_TASK)) {
+        if (numArgs < 3) {
+            dumpHelp(fd);
+            return STATUS_OK;
+        }
+        debugInjectTask(fd, args[1], args[2]);
+    } else if (!strcmp(args[0], COMMAND_STATUS)) {
+        printCurrentStatus(fd);
     } else {
         dumpHelp(fd);
     }
@@ -331,6 +374,37 @@
     return STATUS_OK;
 }
 
+void RemoteAccessService::printCurrentStatus(int fd) {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+    dprintf(fd,
+            "\nRemoteAccess HAL status \n"
+            "Remote task callback registered: %s\n"
+            "Task receiving GRPC connection established: %s\n"
+            "Received task count by clientId: \n%s\n",
+            boolToString(mRemoteTaskCallback.get()).c_str(), boolToString(mGrpcConnected).c_str(),
+            clientIdToTaskCountToStringLocked().c_str());
+}
+
+void RemoteAccessService::debugInjectTask(int fd, std::string_view clientId,
+                                          std::string_view taskData) {
+    std::string clientIdCopy = std::string(clientId);
+    if (auto result = deliverRemoteTaskThroughCallback(clientIdCopy, taskData); !result.ok()) {
+        dprintf(fd, "Failed to inject task: %s", result.error().message().c_str());
+        return;
+    }
+    dprintf(fd, "Task for client: %s, data: [%s] successfully injected\n", clientId.data(),
+            taskData.data());
+}
+
+std::string RemoteAccessService::clientIdToTaskCountToStringLocked() {
+    // Print the table header
+    std::string output = "| ClientId | Count |\n";
+    for (const auto& [clientId, taskCount] : mClientIdToTaskCount) {
+        output += StringPrintf("  %-9s  %-6zu\n", clientId.c_str(), taskCount);
+    }
+    return output;
+}
+
 ScopedAStatus DebugRemoteTaskCallback::onRemoteTaskRequested(const std::string& clientId,
                                                              const std::vector<uint8_t>& data) {
     std::lock_guard<std::mutex> lockGuard(mLock);
diff --git a/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp b/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp
index 8c4fa08..c5afd63 100644
--- a/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp
+++ b/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp
@@ -187,8 +187,8 @@
 
     void setRetryWaitInMs(size_t retryWaitInMs) { mService->setRetryWaitInMs(retryWaitInMs); }
 
-    ScopedAStatus getDeviceIdWithClient(IVhalClient& vhalClient, std::string* deviceId) {
-        return mService->getDeviceIdWithClient(vhalClient, deviceId);
+    ScopedAStatus getVehicleIdWithClient(IVhalClient& vhalClient, std::string* vehicleId) {
+        return mService->getVehicleIdWithClient(vhalClient, vehicleId);
     }
 
   private:
@@ -358,13 +358,13 @@
     std::this_thread::sleep_for(std::chrono::milliseconds(150));
 }
 
-TEST_F(RemoteAccessServiceUnitTest, testGetDeviceId) {
-    std::string deviceId;
+TEST_F(RemoteAccessServiceUnitTest, testGetVehicleId) {
+    std::string vehicleId;
 
     FakeVhalClient vhalClient;
 
-    ASSERT_TRUE(getDeviceIdWithClient(vhalClient, &deviceId).isOk());
-    ASSERT_EQ(deviceId, kTestVin);
+    ASSERT_TRUE(getVehicleIdWithClient(vhalClient, &vehicleId).isOk());
+    ASSERT_EQ(vehicleId, kTestVin);
 }
 
 }  // namespace remoteaccess
diff --git a/automotive/sv/1.0/default/tests/fuzzer/Android.bp b/automotive/sv/1.0/default/tests/fuzzer/Android.bp
index 394c532..696bfad 100644
--- a/automotive/sv/1.0/default/tests/fuzzer/Android.bp
+++ b/automotive/sv/1.0/default/tests/fuzzer/Android.bp
@@ -47,5 +47,13 @@
             "android-media-fuzzing-reports@google.com",
         ],
         componentid: 533764,
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of android.hardware.automotive.sv@1.0-service",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
     },
 }
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 33e211c..586a98e 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -292,5 +292,13 @@
             "android-media-fuzzing-reports@google.com",
         ],
         componentid: 533764,
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of android.hardware.automotive.vehicle@2.0-manager-lib",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
     },
 }
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
index b5026a6..edd4484 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
@@ -756,8 +756,9 @@
     // Clear existing events.
     mEventQueue.flush();
     std::this_thread::sleep_for(std::chrono::milliseconds(500));
-    // There should be no new events generated.
-    EXPECT_EQ((size_t)0, mEventQueue.flush().size());
+    // Technically there should be no new events generated, however, there might still be one event
+    // in the queue while we are stopping the generator.
+    EXPECT_LE(mEventQueue.flush().size(), 1u);
 }
 
 std::string getTestFilePath(const char* filename) {
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index d75b046..5b0a505 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -57,46 +57,25 @@
 };
 
 /**
- * Vehicle Areas
+ * List of different supported area types for vehicle properties.
  * Used to construct property IDs in the VehicleProperty enum.
  *
- * Some properties may be associated with particular vehicle areas. For
- * example, VehicleProperty:DOOR_LOCK property must be associated with
- * particular door, thus this property must be marked with
- * VehicleArea:DOOR flag.
+ * Some properties may be associated with particular areas in the vehicle. For example,
+ * VehicleProperty#DOOR_LOCK property must be associated with a particular door, thus this property
+ * must be of the VehicleArea#DOOR area type.
  *
- * Other properties may not be associated with particular vehicle area.
- * These kinds of properties must have VehicleArea:GLOBAL flag.
+ * Other properties may not be associated with a particular area in the vehicle. These kinds of
+ * properties must be of the VehicleArea#GLOBAL area type.
  *
- * [Definition] Area: An area represents a unique element of an AreaType.
- *   For instance, if AreaType is WINDOW, then an area may be FRONT_WINDSHIELD.
- *
- * [Definition] AreaID: An AreaID is a combination of one or more areas,
- *   and is represented using a bitmask of Area enums. Different AreaTypes may
- *   not be mixed in a single AreaID. For instance, a window area cannot be
- *   combined with a seat area in an AreaID.
- *
- * Rules for mapping a zoned property to AreaIDs:
- *  - A property must be mapped to an array of AreaIDs that are impacted when
- *    the property value changes.
- *  - Each element in the array must represent an AreaID, in which the
- *    property value can only be changed together in all the areas within
- *    the AreaID and never independently. That is, when the property value
- *    changes in one of the areas in an AreaID in the array, then it must
- *    automatically change in all other areas in the AreaID.
- *  - The property value must be independently controllable in any two
- *    different AreaIDs in the array.
- *  - An area must only appear once in the array of AreaIDs. That is, an
- *    area must only be part of a single AreaID in the array.
- *
- * [Definition] Global Property: A property that applies to the entire car
- *   and is not associated with a specific area. For example, FUEL_LEVEL,
- *   HVAC_STEERING_WHEEL_HEAT.
- *
- * Rules for mapping a global property to AreaIDs:
- *  - A global property must not be mapped to AreaIDs.
-*/
+ * Note: This is not the same as areaId used in VehicleAreaConfig. E.g. for a global property, the
+ * property ID is of the VehicleArea#GLOBAL area type, however, the area ID must be 0.
+ */
+// A better name would be VehicleAreaType
 enum VehicleArea : int32_t {
+    /**
+     * A global property is a property that applies to the entire vehicle and is not associated with
+     * a specific area. For example, FUEL_LEVEL, HVAC_STEERING_WHEEL_HEAT are global properties.
+     */
     GLOBAL      = 0x01000000,
     /** WINDOW maps to enum VehicleAreaWindow */
     WINDOW      = 0x03000000,
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
index abd9540..20c046d 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
@@ -20,7 +20,7 @@
 @JavaDerive(equals=true, toString=true)
 parcelable VehicleAreaConfig {
     /**
-     * Area id is ignored for VehiclePropertyGroup:GLOBAL properties.
+     * Area id is always 0 for VehicleArea#GLOBAL properties.
      */
     int areaId;
 
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl
index 1b48f0b..61b9369 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl
@@ -37,7 +37,25 @@
     VehiclePropertyChangeMode changeMode = VehiclePropertyChangeMode.STATIC;
 
     /**
-     * Contains per-area configuration.
+     * Contains per-areaId configuration.
+     *
+     * [Definition] area: An area represents a unique element of a VehicleArea. For instance, if the
+     *   VehicleArea is WINDOW, then an example area is FRONT_WINDSHIELD.
+     *
+     * [Definition] area ID: An area ID is a combination of one or more areas, and is created by
+     *   bitwise "OR"ing the areas together. Areas from different VehicleArea values may not be
+     *   mixed in a single area ID. For example, a VehicleAreaWindow area cannot be combined with a
+     *   VehicleAreaSeat area in an area ID.
+     *
+     * For VehicleArea#GLOBAL properties, they must map only to a single area ID of 0.
+     *
+     * Rules for mapping a non VehicleArea#GLOBAL property to area IDs:
+     *  - A property must be mapped to a set of area IDs that are impacted when the property value
+     *    changes.
+     *  - An area cannot be part of multiple area IDs, it must only be part of a single area ID.
+     *  - When the property value changes in one of the areas in an area ID, then it must
+     *    automatically change in all other areas in the area ID.
+     *  - The property value must be independently controllable in any two different area IDs.
      */
     VehicleAreaConfig[] areaConfigs;
 
diff --git a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
index 28790e2..ae36290 100644
--- a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
+++ b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
@@ -2371,7 +2371,7 @@
                     66.19999694824219,
                     "VehicleUnit::FAHRENHEIT",
                     19.0,
-                    66.5
+                    66.0
                 ]
             }
         },
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
index 6fd2367..7b74092 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
@@ -178,6 +178,13 @@
     void eventFromVehicleBus(
             const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
 
+    int getHvacTempNumIncrements(int requestedTemp, int minTemp, int maxTemp, int increment);
+    void updateHvacTemperatureValueSuggestionInput(
+            const std::vector<int>& hvacTemperatureSetConfigArray,
+            std::vector<float>* hvacTemperatureValueSuggestionInput);
+    VhalResult<void> setHvacTemperatureValueSuggestion(
+            const aidl::android::hardware::automotive::vehicle::VehiclePropValue&
+                    hvacTemperatureValueSuggestion);
     VhalResult<void> maybeSetSpecialValue(
             const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value,
             bool* isSpecialValue);
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 78c21e9..4544389 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -67,6 +67,7 @@
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+using ::aidl::android::hardware::automotive::vehicle::VehicleUnit;
 
 using ::android::base::EqualsIgnoreCase;
 using ::android::base::Error;
@@ -298,6 +299,94 @@
     return {};
 }
 
+int FakeVehicleHardware::getHvacTempNumIncrements(int requestedTemp, int minTemp, int maxTemp,
+                                                  int increment) {
+    requestedTemp = std::max(requestedTemp, minTemp);
+    requestedTemp = std::min(requestedTemp, maxTemp);
+    int numIncrements = (requestedTemp - minTemp) / increment;
+    return numIncrements;
+}
+
+void FakeVehicleHardware::updateHvacTemperatureValueSuggestionInput(
+        const std::vector<int>& hvacTemperatureSetConfigArray,
+        std::vector<float>* hvacTemperatureValueSuggestionInput) {
+    int minTempInCelsius = hvacTemperatureSetConfigArray[0];
+    int maxTempInCelsius = hvacTemperatureSetConfigArray[1];
+    int incrementInCelsius = hvacTemperatureSetConfigArray[2];
+
+    int minTempInFahrenheit = hvacTemperatureSetConfigArray[3];
+    int maxTempInFahrenheit = hvacTemperatureSetConfigArray[4];
+    int incrementInFahrenheit = hvacTemperatureSetConfigArray[5];
+
+    // The HVAC_TEMPERATURE_SET config array values are temperature values that have been multiplied
+    // by 10 and converted to integers. Therefore, requestedTemp must also be multiplied by 10 and
+    // converted to an integer in order for them to be the same units.
+    int requestedTemp = static_cast<int>((*hvacTemperatureValueSuggestionInput)[0] * 10.0f);
+    int numIncrements =
+            (*hvacTemperatureValueSuggestionInput)[1] == toInt(VehicleUnit::CELSIUS)
+                    ? getHvacTempNumIncrements(requestedTemp, minTempInCelsius, maxTempInCelsius,
+                                               incrementInCelsius)
+                    : getHvacTempNumIncrements(requestedTemp, minTempInFahrenheit,
+                                               maxTempInFahrenheit, incrementInFahrenheit);
+
+    int suggestedTempInCelsius = minTempInCelsius + incrementInCelsius * numIncrements;
+    int suggestedTempInFahrenheit = minTempInFahrenheit + incrementInFahrenheit * numIncrements;
+    // HVAC_TEMPERATURE_VALUE_SUGGESTION specifies the temperature values to be in the original
+    // floating point form so we divide by 10 and convert to float.
+    (*hvacTemperatureValueSuggestionInput)[2] = static_cast<float>(suggestedTempInCelsius) / 10.0f;
+    (*hvacTemperatureValueSuggestionInput)[3] =
+            static_cast<float>(suggestedTempInFahrenheit) / 10.0f;
+}
+
+VhalResult<void> FakeVehicleHardware::setHvacTemperatureValueSuggestion(
+        const VehiclePropValue& hvacTemperatureValueSuggestion) {
+    auto hvacTemperatureSetConfigResult =
+            mServerSidePropStore->getConfig(toInt(VehicleProperty::HVAC_TEMPERATURE_SET));
+
+    if (!hvacTemperatureSetConfigResult.ok()) {
+        return StatusError(getErrorCode(hvacTemperatureSetConfigResult)) << StringPrintf(
+                       "Failed to set HVAC_TEMPERATURE_VALUE_SUGGESTION because"
+                       " HVAC_TEMPERATURE_SET could not be retrieved. Error: %s",
+                       getErrorMsg(hvacTemperatureSetConfigResult).c_str());
+    }
+
+    const auto& originalInput = hvacTemperatureValueSuggestion.value.floatValues;
+    if (originalInput.size() != 4) {
+        return StatusError(StatusCode::INVALID_ARG) << StringPrintf(
+                       "Failed to set HVAC_TEMPERATURE_VALUE_SUGGESTION because float"
+                       " array value is not size 4.");
+    }
+
+    bool isTemperatureUnitSpecified = originalInput[1] == toInt(VehicleUnit::CELSIUS) ||
+                                      originalInput[1] == toInt(VehicleUnit::FAHRENHEIT);
+    if (!isTemperatureUnitSpecified) {
+        return StatusError(StatusCode::INVALID_ARG) << StringPrintf(
+                       "Failed to set HVAC_TEMPERATURE_VALUE_SUGGESTION because float"
+                       " value at index 1 is not any of %d or %d, which corresponds to"
+                       " VehicleUnit#CELSIUS and VehicleUnit#FAHRENHEIT respectively.",
+                       toInt(VehicleUnit::CELSIUS), toInt(VehicleUnit::FAHRENHEIT));
+    }
+
+    auto updatedValue = mValuePool->obtain(hvacTemperatureValueSuggestion);
+    const auto& hvacTemperatureSetConfigArray = hvacTemperatureSetConfigResult.value()->configArray;
+    auto& hvacTemperatureValueSuggestionInput = updatedValue->value.floatValues;
+
+    updateHvacTemperatureValueSuggestionInput(hvacTemperatureSetConfigArray,
+                                              &hvacTemperatureValueSuggestionInput);
+
+    updatedValue->timestamp = elapsedRealtimeNano();
+    auto writeResult = mServerSidePropStore->writeValue(std::move(updatedValue),
+                                                        /* updateStatus = */ true,
+                                                        VehiclePropertyStore::EventMode::ALWAYS);
+    if (!writeResult.ok()) {
+        return StatusError(getErrorCode(writeResult))
+               << StringPrintf("failed to write value into property store, error: %s",
+                               getErrorMsg(writeResult).c_str());
+    }
+
+    return {};
+}
+
 bool FakeVehicleHardware::isHvacPropAndHvacNotAvailable(int32_t propId, int32_t areaId) const {
     std::unordered_set<int32_t> powerProps(std::begin(HVAC_POWER_PROPERTIES),
                                            std::end(HVAC_POWER_PROPERTIES));
@@ -491,6 +580,9 @@
         case VENDOR_PROPERTY_ID:
             *isSpecialValue = true;
             return StatusError((StatusCode)VENDOR_ERROR_CODE);
+        case toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION):
+            *isSpecialValue = true;
+            return setHvacTemperatureValueSuggestion(value);
 
 #ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
         case toInt(VehicleProperty::CLUSTER_REPORT_STATE):
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
index 93a63ad..a50b4ad 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -57,6 +57,7 @@
 using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+using ::aidl::android::hardware::automotive::vehicle::VehicleUnit;
 using ::android::base::expected;
 using ::android::base::ScopedLockAssertion;
 using ::android::base::StringPrintf;
@@ -2242,6 +2243,359 @@
     }
 }
 
+TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) {
+    float CELSIUS = static_cast<float>(toInt(VehicleUnit::CELSIUS));
+    float FAHRENHEIT = static_cast<float>(toInt(VehicleUnit::FAHRENHEIT));
+
+    VehiclePropValue floatArraySizeFour = {
+            .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+            .areaId = HVAC_ALL,
+            .value.floatValues = {0, CELSIUS, 0, 0},
+    };
+    StatusCode status = setValue(floatArraySizeFour);
+    EXPECT_EQ(status, StatusCode::OK);
+
+    VehiclePropValue floatArraySizeZero = {
+            .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+            .areaId = HVAC_ALL,
+    };
+    status = setValue(floatArraySizeZero);
+    EXPECT_EQ(status, StatusCode::INVALID_ARG);
+
+    VehiclePropValue floatArraySizeFive = {
+            .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+            .areaId = HVAC_ALL,
+            .value.floatValues = {0, CELSIUS, 0, 0, 0},
+    };
+    status = setValue(floatArraySizeFive);
+    EXPECT_EQ(status, StatusCode::INVALID_ARG);
+
+    VehiclePropValue invalidUnit = {
+            .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+            .areaId = HVAC_ALL,
+            .value.floatValues = {0, 0, 0, 0},
+    };
+    status = setValue(floatArraySizeFive);
+    EXPECT_EQ(status, StatusCode::INVALID_ARG);
+    clearChangedProperties();
+
+    // Config array values from HVAC_TEMPERATURE_SET in DefaultProperties.json
+    auto configs = getHardware()->getAllPropertyConfigs();
+    VehiclePropConfig* hvacTemperatureSetConfig = nullptr;
+    for (auto& config : configs) {
+        if (config.prop == toInt(VehicleProperty::HVAC_TEMPERATURE_SET)) {
+            hvacTemperatureSetConfig = &config;
+            break;
+        }
+    }
+    EXPECT_NE(hvacTemperatureSetConfig, nullptr);
+
+    auto& hvacTemperatureSetConfigArray = hvacTemperatureSetConfig->configArray;
+    // The HVAC_TEMPERATURE_SET config array values are temperature values that have been multiplied
+    // by 10 and converted to integers. HVAC_TEMPERATURE_VALUE_SUGGESTION specifies the temperature
+    // values to be in the original floating point form so we divide by 10 and convert to float.
+    float minTempInCelsius = hvacTemperatureSetConfigArray[0] / 10.0f;
+    float maxTempInCelsius = hvacTemperatureSetConfigArray[1] / 10.0f;
+    float incrementInCelsius = hvacTemperatureSetConfigArray[2] / 10.0f;
+    float minTempInFahrenheit = hvacTemperatureSetConfigArray[3] / 10.0f;
+    float maxTempInFahrenheit = hvacTemperatureSetConfigArray[4] / 10.0f;
+    float incrementInFahrenheit = hvacTemperatureSetConfigArray[5] / 10.0f;
+
+    auto testCases = {
+            SetSpecialValueTestCase{
+                    .name = "min_celsius_temperature",
+                    .valuesToSet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {minTempInCelsius, CELSIUS, 0, 0},
+                                    },
+                            },
+                    .expectedValuesToGet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {minTempInCelsius, CELSIUS,
+                                                                  minTempInCelsius,
+                                                                  minTempInFahrenheit},
+                                    },
+                            },
+            },
+            SetSpecialValueTestCase{
+                    .name = "min_fahrenheit_temperature",
+                    .valuesToSet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {minTempInFahrenheit, FAHRENHEIT,
+                                                                  0, 0},
+                                    },
+                            },
+                    .expectedValuesToGet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {minTempInFahrenheit, FAHRENHEIT,
+                                                                  minTempInCelsius,
+                                                                  minTempInFahrenheit},
+                                    },
+                            },
+            },
+            SetSpecialValueTestCase{
+                    .name = "max_celsius_temperature",
+                    .valuesToSet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {maxTempInCelsius, CELSIUS, 0, 0},
+                                    },
+                            },
+                    .expectedValuesToGet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {maxTempInCelsius, CELSIUS,
+                                                                  maxTempInCelsius,
+                                                                  maxTempInFahrenheit},
+                                    },
+                            },
+            },
+            SetSpecialValueTestCase{
+                    .name = "max_fahrenheit_temperature",
+                    .valuesToSet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {maxTempInFahrenheit, FAHRENHEIT,
+                                                                  0, 0},
+                                    },
+                            },
+                    .expectedValuesToGet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {maxTempInFahrenheit, FAHRENHEIT,
+                                                                  maxTempInCelsius,
+                                                                  maxTempInFahrenheit},
+                                    },
+                            },
+            },
+            SetSpecialValueTestCase{
+                    .name = "below_min_celsius_temperature",
+                    .valuesToSet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {minTempInCelsius - 1, CELSIUS, 0,
+                                                                  0},
+                                    },
+                            },
+                    .expectedValuesToGet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {minTempInCelsius - 1, CELSIUS,
+                                                                  minTempInCelsius,
+                                                                  minTempInFahrenheit},
+                                    },
+                            },
+            },
+            SetSpecialValueTestCase{
+                    .name = "below_min_fahrenheit_temperature",
+                    .valuesToSet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {minTempInFahrenheit - 1,
+                                                                  FAHRENHEIT, 0, 0},
+                                    },
+                            },
+                    .expectedValuesToGet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {minTempInFahrenheit - 1,
+                                                                  FAHRENHEIT, minTempInCelsius,
+                                                                  minTempInFahrenheit},
+                                    },
+                            },
+            },
+            SetSpecialValueTestCase{
+                    .name = "above_max_celsius_temperature",
+                    .valuesToSet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {maxTempInCelsius + 1, CELSIUS, 0,
+                                                                  0},
+                                    },
+                            },
+                    .expectedValuesToGet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {maxTempInCelsius + 1, CELSIUS,
+                                                                  maxTempInCelsius,
+                                                                  maxTempInFahrenheit},
+                                    },
+                            },
+            },
+            SetSpecialValueTestCase{
+                    .name = "above_max_fahrenheit_temperature",
+                    .valuesToSet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {maxTempInFahrenheit + 1,
+                                                                  FAHRENHEIT, 0, 0},
+                                    },
+                            },
+                    .expectedValuesToGet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {maxTempInFahrenheit + 1,
+                                                                  FAHRENHEIT, maxTempInCelsius,
+                                                                  maxTempInFahrenheit},
+                                    },
+                            },
+            },
+            SetSpecialValueTestCase{
+                    .name = "inbetween_value_celsius",
+                    .valuesToSet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {minTempInCelsius +
+                                                                          incrementInCelsius * 2.5f,
+                                                                  CELSIUS, 0, 0},
+                                    },
+                            },
+                    .expectedValuesToGet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues =
+                                                    {minTempInCelsius + incrementInCelsius * 2.5f,
+                                                     CELSIUS,
+                                                     minTempInCelsius + incrementInCelsius * 2,
+                                                     minTempInFahrenheit +
+                                                             incrementInFahrenheit * 2},
+                                    },
+                            },
+            },
+            SetSpecialValueTestCase{
+                    .name = "inbetween_value_fahrenheit",
+                    .valuesToSet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues = {minTempInFahrenheit +
+                                                                          incrementInFahrenheit *
+                                                                                  2.5f,
+                                                                  FAHRENHEIT, 0, 0},
+                                    },
+                            },
+                    .expectedValuesToGet =
+                            {
+                                    VehiclePropValue{
+                                            .prop = toInt(
+                                                    VehicleProperty::
+                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .areaId = HVAC_ALL,
+                                            .value.floatValues =
+                                                    {minTempInFahrenheit +
+                                                             incrementInFahrenheit * 2.5f,
+                                                     FAHRENHEIT,
+                                                     minTempInCelsius + incrementInCelsius * 2,
+                                                     minTempInFahrenheit +
+                                                             incrementInFahrenheit * 2},
+                                    },
+                            },
+            },
+    };
+
+    for (auto& tc : testCases) {
+        StatusCode status = setValue(tc.valuesToSet[0]);
+        EXPECT_EQ(status, StatusCode::OK);
+
+        auto events = getChangedProperties();
+        EXPECT_EQ(events.size(), static_cast<size_t>(1));
+        events[0].timestamp = 0;
+
+        EXPECT_EQ(events[0], (tc.expectedValuesToGet[0]))
+                << "Failed Test: " << tc.name << "\n"
+                << "Received - prop: " << events[0].prop << ", areaId: " << events[0].areaId
+                << ", floatValues: {" << events[0].value.floatValues[0] << ", "
+                << events[0].value.floatValues[1] << ", " << events[0].value.floatValues[2] << ", "
+                << events[0].value.floatValues[3] << "}\n"
+                << "Expected - prop: " << tc.expectedValuesToGet[0].prop
+                << ", areaId: " << tc.expectedValuesToGet[0].areaId << ", floatValues: {"
+                << tc.expectedValuesToGet[0].value.floatValues[0] << ", "
+                << tc.expectedValuesToGet[0].value.floatValues[1] << ", "
+                << tc.expectedValuesToGet[0].value.floatValues[2] << ", "
+                << tc.expectedValuesToGet[0].value.floatValues[3] << "}\n";
+        clearChangedProperties();
+    }
+}
+
 }  // namespace fake
 }  // namespace vehicle
 }  // namespace automotive
diff --git a/automotive/vehicle/aidl/impl/vhal/vhal-default-service.xml b/automotive/vehicle/aidl/impl/vhal/vhal-default-service.xml
index 4d587ee..9834cdb 100644
--- a/automotive/vehicle/aidl/impl/vhal/vhal-default-service.xml
+++ b/automotive/vehicle/aidl/impl/vhal/vhal-default-service.xml
@@ -1,7 +1,7 @@
 <manifest version="1.0" type="device">
     <hal format="aidl">
         <name>android.hardware.automotive.vehicle</name>
-        <version>1</version>
+        <version>2</version>
         <fqname>IVehicle/default</fqname>
     </hal>
 </manifest>
diff --git a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleArea.aidl b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleArea.aidl
index dab0349..6f7f783 100644
--- a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleArea.aidl
+++ b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleArea.aidl
@@ -16,9 +16,28 @@
 
 package android.hardware.automotive.vehicle;
 
+/**
+ * List of different supported area types for vehicle properties.
+ * Used to construct property IDs in the VehicleProperty enum.
+ *
+ * Some properties may be associated with particular areas in the vehicle. For example,
+ * VehicleProperty#DOOR_LOCK property must be associated with a particular door, thus this property
+ * must be of the VehicleArea#DOOR area type.
+ *
+ * Other properties may not be associated with a particular area in the vehicle. These kinds of
+ * properties must be of the VehicleArea#GLOBAL area type.
+ *
+ * Note: This is not the same as areaId used in VehicleAreaConfig. E.g. for a global property, the
+ * property ID is of the VehicleArea#GLOBAL area type, however, the area ID must be 0.
+ */
 @VintfStability
 @Backing(type="int")
+// A better name would be VehicleAreaType
 enum VehicleArea {
+    /**
+     * A global property is a property that applies to the entire vehicle and is not associated with
+     * a specific area. For example, FUEL_LEVEL, HVAC_STEERING_WHEEL_HEAT are global properties.
+     */
     GLOBAL = 0x01000000,
     /** WINDOW maps to enum VehicleAreaWindow */
     WINDOW = 0x03000000,
diff --git a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleHvacFanDirection.aidl b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleHvacFanDirection.aidl
index fa5d711..f5b77de 100644
--- a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleHvacFanDirection.aidl
+++ b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleHvacFanDirection.aidl
@@ -29,6 +29,9 @@
      * FACE_AND_FLOOR = FACE | FLOOR
      */
     FACE_AND_FLOOR = 0x3,
+    /**
+     * DEFROST may also be described as the windshield fan direction.
+     */
     DEFROST = 0x4,
     /**
      * DEFROST_AND_FLOOR = DEFROST | FLOOR
diff --git a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
index eeafaaf..8a3ab90 100644
--- a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -340,6 +340,9 @@
     /**
      * Fuel door open
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -375,6 +378,9 @@
     /**
      * EV charge port open
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -407,9 +413,9 @@
      * all energy sources in a vehicle.  For example, a hybrid car's range will
      * be the sum of the ranges based on fuel and battery.
      *
-     * This property may be writable because a navigation app could update the range if it has a
-     * more accurate estimate based on the upcoming route. However, this property can be set to
-     * VehiclePropertyAccess.READ only at the OEM's discretion.
+     * This property is defined as VehiclePropertyAccess.READ_WRITE because a navigation app could
+     * update the range if it has a more accurate estimate based on the upcoming route. However,
+     * this property can be implemented as VehiclePropertyAccess.READ only at the OEM's discretion.
      *
      * @change_mode VehiclePropertyChangeMode.CONTINUOUS
      * @access VehiclePropertyAccess.READ_WRITE
@@ -465,6 +471,9 @@
      * If true, the vehicle may automatically shut off the engine when it is not needed and then
      * automatically restart it when needed.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -544,6 +553,9 @@
      * regenerated from braking. The minInt32Value in default area's VehicleAreaConfig indicates no
      * regenerative braking.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -629,6 +641,9 @@
      *
      * The EvStoppingMode enum may be extended to include more states in the future.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum EvStoppingMode
@@ -683,6 +698,9 @@
      *
      * Fan speed setting
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -691,6 +709,9 @@
     /**
      * Fan direction setting
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleHvacFanDirection
@@ -727,6 +748,9 @@
      * that property to get the suggested value before setting HVAC_TEMPERATURE_SET. Otherwise,
      * the application may choose the value in HVAC_TEMPERATURE_SET configArray by itself.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @unit VehicleUnit:CELSIUS
@@ -736,6 +760,9 @@
     /**
      * Fan-based defrost for designated window.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -744,6 +771,9 @@
     /**
      * On/off AC for designated areaId
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @config_flags Supported areaIds
@@ -758,6 +788,9 @@
      * Any parameters modified as a side effect of turning on/off the MAX AC
      * parameter shall generate onPropertyEvent() callbacks to the VHAL.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -777,6 +810,9 @@
      * areaConfig.areaId = {ROW_1_LEFT | ROW_1_RIGHT} indicates HVAC_MAX_DEFROST_ON
      * only can be controlled for the front rows.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -790,6 +826,9 @@
      * Recirc “off” means the majority of the airflow into the cabin is coming
      * from outside the car.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -825,6 +864,9 @@
      * onPropertyEvent() callbacks (i.e. HVAC_DUAL_ON = false,
      * HVAC_TEMPERATURE_SET[AreaID] = xxx, etc).
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -833,6 +875,9 @@
     /**
      * On/off automatic mode
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -849,6 +894,9 @@
      * min/max range defines the allowable range and number of steps in each
      * direction.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -861,6 +909,9 @@
      * The Max value in the config data represents the highest heating level.
      * The Min value in the config data MUST be zero and indicates no heating.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -875,6 +926,9 @@
      * Negative value indicates cooling.
      * 0 indicates temperature control is off.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -893,6 +947,12 @@
      * Values must be one of VehicleUnit::CELSIUS or VehicleUnit::FAHRENHEIT
      * Note that internally, all temperatures are represented in floating point Celsius.
      *
+     * If updating HVAC_TEMPERATURE_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS
+     * properties, then their values must be updated and communicated to the AAOS framework as well.
+     *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleUnit
@@ -944,6 +1004,9 @@
      *   - ROW_1_LEFT | ROW_1_RIGHT
      *   - ROW_2_LEFT | ROW_2_CENTER | ROW_2_RIGHT | ROW_3_LEFT | ROW_3_CENTER | ROW_3_RIGHT
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -975,6 +1038,9 @@
      * switch to recirculation mode if the vehicle detects poor incoming air
      * quality.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -990,6 +1056,9 @@
      * ventilation.  This is different than seating cooling. It can be on at the
      * same time as cooling, or not.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -998,6 +1067,9 @@
     /**
      * Electric defrosters' status
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1054,6 +1126,13 @@
      * For example: configArray[0] = METER
      *              configArray[1] = KILOMETER
      *              configArray[2] = MILE
+     *
+     * If updating DISTANCE_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS properties,
+     * then their values must be updated and communicated to the AAOS framework as well.
+     *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleUnit
@@ -1070,6 +1149,13 @@
      * Volume units are defined in VehicleUnit.
      * For example: configArray[0] = LITER
      *              configArray[1] = GALLON
+     *
+     * If updating FUEL_VOLUME_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS properties,
+     * then their values must be updated and communicated to the AAOS framework as well.
+     *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleUnit
@@ -1087,6 +1173,13 @@
      * For example: configArray[0] = KILOPASCAL
      *              configArray[1] = PSI
      *              configArray[2] = BAR
+     *
+     * If updating TIRE_PRESSURE_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS
+     * properties, then their values must be updated and communicated to the AAOS framework as well.
+     *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleUnit
@@ -1104,6 +1197,13 @@
      * For example: configArray[0] = WATT_HOUR
      *              configArray[1] = AMPERE_HOURS
      *              configArray[2] = KILOWATT_HOUR
+     *
+     * If updating EV_BATTERY_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS properties,
+     * then their values must be updated and communicated to the AAOS framework as well.
+     *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleUnit
@@ -1117,6 +1217,9 @@
      * True indicates units are distance over volume such as MPG.
      * False indicates units are volume over distance such as L/100KM.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1132,6 +1235,13 @@
      * For example: configArray[0] = METER_PER_SEC
      *              configArray[1] = MILES_PER_HOUR
      *              configArray[2] = KILOMETERS_PER_HOUR
+     *
+     * If updating VEHICLE_SPEED_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS
+     * properties, then their values must be updated and communicated to the AAOS framework as well.
+     *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1259,7 +1369,6 @@
      *
      *   int32Values[0] : VehicleApPowerStateReport enum value
      *   int32Values[1] : Time in ms to wake up, if necessary.  Otherwise 0.
-
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
@@ -1468,9 +1577,12 @@
      * This is an integer in case a door may be set to a particular position.
      * Max value indicates fully open, min value (0) indicates fully closed.
      *
-     * Some vehicles (minivans) can open the door electronically.  Hence, the
+     * Some vehicles (minivans) can open the door electronically. Hence, the
      * ability to write this property.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1479,6 +1591,9 @@
     /**
      * Door move
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1489,6 +1604,9 @@
      *
      * 'true' indicates door is locked
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1501,6 +1619,9 @@
      *
      * If enabled, the door is unable to be opened from the inside.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1511,6 +1632,9 @@
      *
      * Positive value indicates tilt upwards, negative value is downwards
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1521,6 +1645,9 @@
      *
      * Positive value indicates tilt upwards, negative value is downwards
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1531,6 +1658,9 @@
      *
      * Positive value indicate tilt right, negative value is left
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1541,6 +1671,9 @@
      *
      * Positive value indicate tilt right, negative value is left
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1551,6 +1684,9 @@
      *
      * True indicates mirror positions are locked and not changeable
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1561,6 +1697,9 @@
      *
      * True indicates mirrors are folded
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1574,6 +1713,9 @@
      * (for example, when the mirrors fold inward automatically when one exits and locks the
      * vehicle) is enabled.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1588,6 +1730,9 @@
      * (for example, when the mirrors tilt downward automatically when one reverses the vehicle) is
      * enabled.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1631,6 +1776,9 @@
      * Write access indicates automatic seat buckling capabilities.  There are
      * no known cars at this time, but you never know...
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1643,6 +1791,9 @@
      * Max value indicates highest position
      * Min value indicates lowest position
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1651,6 +1802,9 @@
     /**
      * Seatbelt height move
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1663,6 +1817,9 @@
      * Max value indicates closest to wheel, min value indicates most rearward
      * position.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1673,6 +1830,9 @@
      *
      * Moves the seat position forward and aft.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1685,6 +1845,9 @@
      * Max value indicates angling forward towards the steering wheel.
      * Min value indicates full recline.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1695,6 +1858,9 @@
      *
      * Moves the backrest forward or recline.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1707,6 +1873,9 @@
      * Max value indicates angling forward towards the steering wheel.
      * Min value indicates full recline.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1717,6 +1886,9 @@
      *
      * Moves the backrest forward or recline.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1729,6 +1901,9 @@
      * Max value indicates highest position.
      * Min value indicates lowest position.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1739,6 +1914,9 @@
      *
      * Moves the seat height.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1751,6 +1929,9 @@
      * Max value indicates longest depth position.
      * Min value indicates shortest position.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1761,6 +1942,9 @@
      *
      * Adjusts the seat depth.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1773,6 +1957,9 @@
      * Max value indicates front edge of seat higher than back edge.
      * Min value indicates front edge of seat lower than back edge.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1783,6 +1970,9 @@
      *
      * Tilts the seat.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1795,6 +1985,9 @@
      * Max value indicates most forward position.
      * Min value indicates most rearward position.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1805,6 +1998,9 @@
      *
      * Adjusts the lumbar support.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1817,6 +2013,9 @@
      * Max value indicates widest lumbar setting (i.e. least support)
      * Min value indicates thinnest lumbar setting.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1827,6 +2026,9 @@
      *
      * Adjusts the amount of lateral lumbar support.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1843,6 +2045,9 @@
      * Max value indicates tallest setting.
      * Min value indicates shortest setting.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1861,6 +2066,9 @@
      *
      * This value is not in any particular unit but in a specified range of steps.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1872,6 +2080,9 @@
      *
      * Moves the headrest up and down.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1884,6 +2095,9 @@
      * Max value indicates most upright angle.
      * Min value indicates shallowest headrest angle.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1894,6 +2108,9 @@
      *
      * Adjusts the angle of the headrest
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1906,6 +2123,9 @@
      * Max value indicates position closest to front of car.
      * Min value indicates position closest to rear of car.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1914,6 +2134,9 @@
     /**
      * Headrest fore/aft move
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1955,6 +2178,9 @@
      * For each supported area ID, the VehicleAreaConfig#supportedEnumValues must be defined unless
      * all enum values of VehicleLightSwitch are supported.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleLightSwitch
@@ -1968,6 +2194,9 @@
      * exit the vehicle. Each area ID must map to the seat that the user is trying to enter/exit
      * with the help of the easy access feature.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -1984,6 +2213,9 @@
      * This property can be set to VehiclePropertyAccess.READ read only for the sake of regulation
      * or safety concerns.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2000,6 +2232,9 @@
      *
      * This value is not in any particular unit but in a specified range of steps.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2019,6 +2254,9 @@
      * Larger absolute values, either positive or negative, indicate a faster movement speed. Once
      * the seat cushion side support reaches the positional limit, the value resets to 0.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2035,6 +2273,9 @@
      *
      * This value is not in any particular unit but in a specified range of steps.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2056,6 +2297,9 @@
      *
      * This value is not in any particular unit but in a specified range of steps.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2077,6 +2321,9 @@
      * The area ID must match the seat that actually moves when the walk-in feature activates, not
      * the intended seat the passengers will sit in.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2108,6 +2355,9 @@
      *
      *  Note that in this mode, 0 indicates the window is closed.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2136,6 +2386,9 @@
      *   Max = open the sunroof, automatically stop when sunroof is fully open.
      *   Min = open the vent, automatically stop when vent is fully open.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2146,6 +2399,9 @@
      *
      * True indicates windows are locked and can't be moved.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2203,12 +2459,12 @@
      * unless all states in WindshieldWipersSwitch are supported (including OTHER, which is not
      * recommended).
      *
-     * This property is defined as read_write, but OEMs have the option to implement it as read
-     * only.
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
      *
-     * If this property is implemented as read_write and the OTHER state is listed in the
-     * VehicleAreaConfig#supportedEnumValues array, then OTHER is not a supported value for writing.
-     * It is only a supported value for reading.
+     * If this property is implemented as VehiclePropertyAccess.READ_WRITE and the OTHER state is
+     * listed in the VehicleAreaConfig#supportedEnumValues array, then OTHER is not a supported
+     * value for writing. It is only a supported value for reading.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
@@ -2231,6 +2487,9 @@
      *
      * This value is not in any particular unit but in a specified range of steps.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2250,6 +2509,9 @@
      *
      * This value is not in any particular unit but in a specified range of steps.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2267,6 +2529,9 @@
      *
      * This value is not in any particular unit but in a specified range of steps.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2285,6 +2550,9 @@
      *
      * This value is not in any particular unit but in a specified range of steps.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2296,6 +2564,9 @@
      * If true, the steering wheel will lock automatically to prevent theft in certain
      * situations.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2306,6 +2577,9 @@
      *
      * If true, the steering wheel's position is locked and not changeable.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2317,6 +2591,9 @@
      * If true, the driver’s steering wheel will automatically adjust to make it easier for the
      * driver to enter and exit the vehicle.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2339,6 +2616,9 @@
      * front right dashboard has a glove box embedded in it, then the area ID should be
      * SEAT_1_RIGHT).
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2354,6 +2634,9 @@
      * front right dashboard has a glove box embedded in it, then the area ID should be
      * VehicleAreaSeat#ROW_1_RIGHT).
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -2385,7 +2668,12 @@
      * when computing the vehicle's location that is shared with Android through the GNSS HAL.
      *
      * The value must return a collection of bit flags. The bit flags are defined in
-     * LocationCharacterization.
+     * LocationCharacterization. The value must also include exactly one of DEAD_RECKONED or
+     * RAW_GNSS_ONLY among its collection of bit flags.
+     *
+     * When this property is not supported, it is assumed that no additional sensor inputs are fused
+     * into the GNSS updates provided through the GNSS HAL. That is unless otherwise specified
+     * through the GNSS HAL interfaces.
      *
      * @change_mode VehiclePropertyChangeMode.STATIC
      * @access VehiclePropertyAccess.READ
@@ -2565,6 +2853,9 @@
      *
      * The setting that the user wants.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleLightSwitch
@@ -2576,6 +2867,9 @@
      *
      * The setting that the user wants.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleLightSwitch
@@ -2603,6 +2897,9 @@
      * Only one of FOG_LIGHTS_SWITCH or REAR_FOG_LIGHTS_SWITCH must be implemented and not both.
      * FRONT_FOG_LIGHTS_SWITCH must not be implemented.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleLightSwitch
@@ -2614,6 +2911,9 @@
      *
      * The setting that the user wants.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleLightSwitch
@@ -2639,6 +2939,9 @@
      * is open or because of a voice command.
      * For example, while the switch is in the "off" or "automatic" position.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleLightSwitch
@@ -2664,6 +2967,9 @@
      * is open or because of a voice command.
      * For example, while the switch is in the "off" or "automatic" position.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleLightSwitch
@@ -2706,6 +3012,9 @@
      * For the global area ID (0), the VehicleAreaConfig#supportedEnumValues must be defined unless
      * all enum values of VehicleLightSwitch are supported.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleLightSwitch
@@ -3372,6 +3681,9 @@
      * Only one of FOG_LIGHTS_SWITCH or FRONT_FOG_LIGHTS_SWITCH must be implemented. Please refer to
      * the documentation on FOG_LIGHTS_SWITCH for more information.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleLightSwitch
@@ -3400,6 +3712,9 @@
      * Only one of FOG_LIGHTS_SWITCH or REAR_FOG_LIGHTS_SWITCH must be implemented. Please refer to
      * the documentation on FOG_LIGHTS_SWITCH for more information.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum VehicleLightSwitch
@@ -3413,6 +3728,9 @@
      * configArray[0] is used to specify the max current draw allowed by
      * the vehicle in Amperes.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @unit VehicleUnit:AMPERE
@@ -3431,6 +3749,9 @@
      *   then the configArray should be {20, 40, 60, 80, 100}
      * If the configArray is empty then all values from 0 to 100 must be valid.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -3455,6 +3776,9 @@
      * The setting that the user wants. Setting this property to true starts the battery charging
      * and setting to false stops charging.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      */
@@ -3647,8 +3971,8 @@
      * low, that information must be conveyed through the ErrorState values in the
      * AUTOMATIC_EMERGENCY_BRAKING_STATE property.
      *
-     * This property is defined as read_write, but OEMs have the option to implement it as read
-     * only.
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
@@ -3689,8 +4013,8 @@
      * low, that information must be conveyed through the ErrorState values in the
      * FORWARD_COLLISION_WARNING_STATE property.
      *
-     * This property is defined as read_write, but OEMs have the option to implement it as read
-     * only.
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
@@ -3728,8 +4052,8 @@
      * information must be conveyed through the ErrorState values in the BLIND_SPOT_WARNING_STATE
      * property.
      *
-     * This property is defined as read_write, but OEMs have the option to implement it as read
-     * only.
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
@@ -3768,8 +4092,8 @@
      * high, that information must be conveyed through the ErrorState values in the
      * LANE_DEPARTURE_WARNING_STATE property.
      *
-     * This property is defined as read_write, but OEMs have the option to implement it as read
-     * only.
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
@@ -3812,8 +4136,8 @@
      * high, that information must be conveyed through the ErrorState values in the
      * LANE_KEEP_ASSIST_STATE property.
      *
-     * This property is defined as read_write, but OEMs have the option to implement it as read
-     * only.
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
@@ -3860,8 +4184,8 @@
      * high, that information must be conveyed through the ErrorState values in the
      * LANE_CENTERING_ASSIST_STATE property.
      *
-     * This property is defined as read_write, but OEMs have the option to implement it as read
-     * only.
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
@@ -3928,8 +4252,8 @@
      * low, that information must be conveyed through the ErrorState values in the
      * EMERGENCY_LANE_KEEP_ASSIST_STATE property.
      *
-     * This property is defined as read_write, but OEMs have the option to implement it as read
-     * only.
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
@@ -3971,8 +4295,8 @@
      * information must be conveyed through the ErrorState values in the CRUISE_CONTROL_STATE
      * property.
      *
-     * This property is defined as read_write, but OEMs have the option to implement it as read
-     * only.
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
@@ -3996,6 +4320,9 @@
      * Trying to write CruiseControlType#OTHER or an ErrorState to this property will throw an
      * IllegalArgumentException.
      *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
+     *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
      * @data_enum CruiseControlType
@@ -4031,7 +4358,10 @@
      *
      * For the global area ID (0), the VehicleAreaConfig#supportedEnumValues array must be defined
      * unless all states of CruiseControlState are supported. Any unsupported commands sent through
-     * this property should return StatusCode.INVALID_ARG.
+     * this property must return StatusCode#INVALID_ARG.
+     *
+     * When this property is not available because CC is disabled (i.e. CRUISE_CONTROL_ENABLED is
+     * false), this property must return StatusCode#NOT_AVAILABLE_DISABLED.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.WRITE
@@ -4049,8 +4379,8 @@
      * The maxFloatValue represents the upper bound of the target speed.
      * The minFloatValue represents the lower bound of the target speed.
      *
-     * When this property is not available (for example when CRUISE_CONTROL_ENABLED is false), it
-     * should return StatusCode.NOT_AVAILABLE_DISABLED.
+     * When this property is not available because CC is disabled (i.e. CRUISE_CONTROL_ENABLED is
+     * false), this property must return StatusCode#NOT_AVAILABLE_DISABLED.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ
@@ -4072,7 +4402,11 @@
      * ascending order. All values must be positive. If the property is writable, all values must be
      * writable.
      *
-     * Writing to this property when it is not available should return StatusCode.NOT_AVAILABLE.
+     * When this property is not available because CC is disabled (i.e. CRUISE_CONTROL_ENABLED is
+     * false), this property must return StatusCode#NOT_AVAILABLE_DISABLED.
+     *
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
@@ -4096,6 +4430,9 @@
      * vehicle is too far away for the sensor to detect), this property should return
      * StatusCode.NOT_AVAILABLE.
      *
+     * When this property is not available because CC is disabled (i.e. CRUISE_CONTROL_ENABLED is
+     * false), this property must return StatusCode#NOT_AVAILABLE_DISABLED.
+     *
      * @change_mode VehiclePropertyChangeMode.CONTINUOUS
      * @access VehiclePropertyAccess.READ
      * @unit VehicleUnit:MILLIMETER
@@ -4114,8 +4451,8 @@
      * not available due to some temporary state, that information must be conveyed through the
      * ErrorState values in the HANDS_ON_DETECTION_STATE property.
      *
-     * This property is defined as read_write, but OEMs have the option to implement it as read
-     * only.
+     * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+     * implement it as VehiclePropertyAccess.READ only.
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.READ_WRITE
diff --git a/bluetooth/1.0/default/test/fuzzer/Android.bp b/bluetooth/1.0/default/test/fuzzer/Android.bp
index 691136f..de2b46d 100644
--- a/bluetooth/1.0/default/test/fuzzer/Android.bp
+++ b/bluetooth/1.0/default/test/fuzzer/Android.bp
@@ -60,5 +60,13 @@
             "android-media-fuzzing-reports@google.com",
         ],
         componentid: 533764,
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of android.hardware.bluetooth@1.0-impl library",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
     },
 }
diff --git a/bluetooth/aidl/TEST_MAPPING b/bluetooth/aidl/TEST_MAPPING
index 342a1e4..f059c04 100644
--- a/bluetooth/aidl/TEST_MAPPING
+++ b/bluetooth/aidl/TEST_MAPPING
@@ -1,7 +1,13 @@
 {
   "presubmit" : [
     {
-      "name" : "VtsHalBluetoothTargetTest"
+      "name" : "VtsHalBluetoothTargetTest",
+      "options": [
+        {
+          // TODO(b/275847929)
+          "exclude-filter": "VtsHalBluetoothTargetTest.PerInstance/BluetoothAidlTest#Cdd_C_12_1_Bluetooth5Requirements/0_android_hardware_bluetooth_IBluetoothHci_default"
+        }
+      ]
     }
   ]
 }
diff --git a/bluetooth/aidl/vts/Android.bp b/bluetooth/aidl/vts/Android.bp
index 414f707..5fc0b2e 100644
--- a/bluetooth/aidl/vts/Android.bp
+++ b/bluetooth/aidl/vts/Android.bp
@@ -13,7 +13,17 @@
         "VtsHalTargetTestDefaults",
         "use_libaidlvintf_gtest_helper_static",
     ],
-    srcs: ["VtsHalBluetoothTargetTest.cpp"],
+    srcs: [
+        "VtsHalBluetoothTargetTest.cpp",
+        ":BluetoothPacketSources",
+        ":BluetoothHciPacketSources",
+    ],
+    generated_headers: [
+        "BluetoothGeneratedPackets_h",
+    ],
+    include_dirs: [
+        "packages/modules/Bluetooth/system/gd",
+    ],
     shared_libs: [
         "libbase",
         "libbinder_ndk",
@@ -45,4 +55,8 @@
     tidy_flags: [
         "--header-filter=^.*tools\\/rootcanal\\/(model|include|net|desktop)\\/(.(?!\\.pb\\.h))*$",
     ],
+    tidy_disabled_srcs: [
+        ":BluetoothPacketSources",
+        ":BluetoothHciPacketSources",
+    ],
 }
diff --git a/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
index 3704c3d..529e092 100644
--- a/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
+++ b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
@@ -24,27 +24,50 @@
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
 #include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
 
 #include <atomic>
 #include <chrono>
 #include <condition_variable>
 #include <future>
-#include <mutex>
 #include <queue>
 #include <thread>
+#include <utility>
 #include <vector>
 
+// TODO: Remove custom logging defines from PDL packets.
+#undef LOG_INFO
+#undef LOG_DEBUG
+#undef LOG_TAG
+#define LOG_TAG "VtsHalBluetooth"
+#include "hci/hci_packets.h"
+#include "packet/raw_builder.h"
+
 using aidl::android::hardware::bluetooth::IBluetoothHci;
 using aidl::android::hardware::bluetooth::IBluetoothHciCallbacks;
 using aidl::android::hardware::bluetooth::Status;
 using ndk::ScopedAStatus;
 using ndk::SpAIBinder;
 
-// Bluetooth Core Specification 3.0 + HS
-static constexpr uint8_t kHciMinimumHciVersion = 5;
-// Bluetooth Core Specification 3.0 + HS
-static constexpr uint8_t kHciMinimumLmpVersion = 5;
+using ::bluetooth::hci::CommandBuilder;
+using ::bluetooth::hci::CommandCompleteView;
+using ::bluetooth::hci::CommandView;
+using ::bluetooth::hci::ErrorCode;
+using ::bluetooth::hci::EventView;
+using ::bluetooth::hci::LeReadLocalSupportedFeaturesBuilder;
+using ::bluetooth::hci::LeReadLocalSupportedFeaturesCompleteView;
+using ::bluetooth::hci::LeReadNumberOfSupportedAdvertisingSetsBuilder;
+using ::bluetooth::hci::LeReadNumberOfSupportedAdvertisingSetsCompleteView;
+using ::bluetooth::hci::LeReadResolvingListSizeBuilder;
+using ::bluetooth::hci::LeReadResolvingListSizeCompleteView;
+using ::bluetooth::hci::LLFeaturesBits;
+using ::bluetooth::hci::OpCode;
+using ::bluetooth::hci::OpCodeText;
+using ::bluetooth::hci::PacketView;
+using ::bluetooth::hci::ReadLocalVersionInformationBuilder;
+using ::bluetooth::hci::ReadLocalVersionInformationCompleteView;
+
+static constexpr uint8_t kMinLeAdvSetForBt5 = 16;
+static constexpr uint8_t kMinLeResolvingListForBt5 = 8;
 
 static constexpr size_t kNumHciCommandsBandwidth = 100;
 static constexpr size_t kNumScoPacketsBandwidth = 100;
@@ -55,65 +78,14 @@
 static constexpr std::chrono::milliseconds kWaitForAclDataTimeout(1000);
 static constexpr std::chrono::milliseconds kInterfaceCloseDelayMs(200);
 
-static constexpr uint8_t kCommandHciShouldBeUnknown[] = {
-    0xff, 0x3B, 0x08, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
-static constexpr uint8_t kCommandHciReadLocalVersionInformation[] = {0x01, 0x10,
-                                                                     0x00};
-static constexpr uint8_t kCommandHciReadBufferSize[] = {0x05, 0x10, 0x00};
-static constexpr uint8_t kCommandHciWriteLoopbackModeLocal[] = {0x02, 0x18,
-                                                                0x01, 0x01};
-static constexpr uint8_t kCommandHciReset[] = {0x03, 0x0c, 0x00};
-static constexpr uint8_t kCommandHciSynchronousFlowControlEnable[] = {
-    0x2f, 0x0c, 0x01, 0x01};
-static constexpr uint8_t kCommandHciWriteLocalName[] = {0x13, 0x0c, 0xf8};
-static constexpr uint8_t kHciStatusSuccess = 0x00;
-static constexpr uint8_t kHciStatusUnknownHciCommand = 0x01;
-
-static constexpr uint8_t kEventConnectionComplete = 0x03;
-static constexpr uint8_t kEventCommandComplete = 0x0e;
-static constexpr uint8_t kEventCommandStatus = 0x0f;
-static constexpr uint8_t kEventNumberOfCompletedPackets = 0x13;
-static constexpr uint8_t kEventLoopbackCommand = 0x19;
-
-static constexpr size_t kEventCodeByte = 0;
-static constexpr size_t kEventLengthByte = 1;
-static constexpr size_t kEventFirstPayloadByte = 2;
-static constexpr size_t kEventCommandStatusStatusByte = 2;
-static constexpr size_t kEventCommandStatusOpcodeLsByte = 4;    // Bytes 4 and 5
-static constexpr size_t kEventCommandCompleteOpcodeLsByte = 3;  // Bytes 3 and 4
-static constexpr size_t kEventCommandCompleteStatusByte = 5;
-static constexpr size_t kEventCommandCompleteFirstParamByte = 6;
-static constexpr size_t kEventLocalHciVersionByte =
-    kEventCommandCompleteFirstParamByte;
-static constexpr size_t kEventLocalLmpVersionByte =
-    kEventLocalHciVersionByte + 3;
-
-static constexpr size_t kEventConnectionCompleteParamLength = 11;
-static constexpr size_t kEventConnectionCompleteType = 11;
-static constexpr size_t kEventConnectionCompleteTypeSco = 0;
-static constexpr size_t kEventConnectionCompleteTypeAcl = 1;
-static constexpr size_t kEventConnectionCompleteHandleLsByte = 3;
-
-static constexpr size_t kEventNumberOfCompletedPacketsNumHandles = 2;
-
-static constexpr size_t kAclBroadcastFlagOffset = 6;
-static constexpr uint8_t kAclBroadcastFlagPointToPoint = 0x0;
-static constexpr uint8_t kAclBroadcastPointToPoint =
-    (kAclBroadcastFlagPointToPoint << kAclBroadcastFlagOffset);
-
-static constexpr uint8_t kAclPacketBoundaryFlagOffset = 4;
-static constexpr uint8_t kAclPacketBoundaryFlagFirstAutoFlushable = 0x2;
-static constexpr uint8_t kAclPacketBoundaryFirstAutoFlushable =
-    kAclPacketBoundaryFlagFirstAutoFlushable << kAclPacketBoundaryFlagOffset;
-
 // To discard Qualcomm ACL debugging
 static constexpr uint16_t kAclHandleQcaDebugMessage = 0xedc;
 
 class ThroughputLogger {
  public:
-  ThroughputLogger(std::string task)
+  explicit ThroughputLogger(std::string task)
       : total_bytes_(0),
-        task_(task),
+        task_(std::move(task)),
         start_time_(std::chrono::steady_clock::now()) {}
 
   ~ThroughputLogger() {
@@ -142,7 +114,7 @@
 // The main test class for Bluetooth HAL.
 class BluetoothAidlTest : public ::testing::TestWithParam<std::string> {
  public:
-  virtual void SetUp() override {
+  void SetUp() override {
     // currently test passthrough mode only
     hci = IBluetoothHci::fromBinder(
         SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
@@ -158,7 +130,7 @@
     ASSERT_NE(bluetooth_hci_death_recipient, nullptr);
     ASSERT_EQ(STATUS_OK,
               AIBinder_linkToDeath(hci->asBinder().get(),
-                                   bluetooth_hci_death_recipient, 0));
+                                   bluetooth_hci_death_recipient, nullptr));
 
     hci_cb = ndk::SharedRefBase::make<BluetoothHciCallbacks>(*this);
     ASSERT_NE(hci_cb, nullptr);
@@ -179,7 +151,7 @@
     ASSERT_TRUE(future.get());
   }
 
-  virtual void TearDown() override {
+  void TearDown() override {
     ALOGI("TearDown");
     // Should not be checked in production code
     ASSERT_TRUE(hci->close().isOk());
@@ -205,8 +177,14 @@
   void handle_no_ops();
   void discard_qca_debugging();
   void wait_for_event(bool timeout_is_error);
-  void wait_for_command_complete_event(std::vector<uint8_t> cmd);
+  void wait_for_command_complete_event(OpCode opCode,
+                                       std::vector<uint8_t>& complete_event);
+  // Wait until a command complete is received.
+  // Command complete will be consumed after this method
+  void wait_and_validate_command_complete_event(OpCode opCode);
   int wait_for_completed_packets_event(uint16_t handle);
+  void send_and_wait_for_cmd_complete(std::unique_ptr<CommandBuilder> cmd,
+                                      std::vector<uint8_t>& cmd_complete);
 
   // A simple test implementation of BluetoothHciCallbacks.
   class BluetoothHciCallbacks
@@ -214,36 +192,41 @@
     BluetoothAidlTest& parent_;
 
    public:
-    BluetoothHciCallbacks(BluetoothAidlTest& parent) : parent_(parent){};
+    explicit BluetoothHciCallbacks(BluetoothAidlTest& parent)
+        : parent_(parent){};
 
-    virtual ~BluetoothHciCallbacks() = default;
+    ~BluetoothHciCallbacks() override = default;
 
-    ndk::ScopedAStatus initializationComplete(Status status) {
+    ndk::ScopedAStatus initializationComplete(Status status) override {
       parent_.initialized_promise.set_value(status == Status::SUCCESS);
       ALOGV("%s (status = %d)", __func__, static_cast<int>(status));
       return ScopedAStatus::ok();
     };
 
-    ndk::ScopedAStatus hciEventReceived(const std::vector<uint8_t>& event) {
+    ndk::ScopedAStatus hciEventReceived(
+        const std::vector<uint8_t>& event) override {
       parent_.event_cb_count++;
       parent_.event_queue.push(event);
-      ALOGV("Event received (length = %d)", static_cast<int>(event.size()));
+      ALOGI("Event received (length = %d)", static_cast<int>(event.size()));
       return ScopedAStatus::ok();
     };
 
-    ndk::ScopedAStatus aclDataReceived(const std::vector<uint8_t>& data) {
+    ndk::ScopedAStatus aclDataReceived(
+        const std::vector<uint8_t>& data) override {
       parent_.acl_cb_count++;
       parent_.acl_queue.push(data);
       return ScopedAStatus::ok();
     };
 
-    ndk::ScopedAStatus scoDataReceived(const std::vector<uint8_t>& data) {
+    ndk::ScopedAStatus scoDataReceived(
+        const std::vector<uint8_t>& data) override {
       parent_.sco_cb_count++;
       parent_.sco_queue.push(data);
       return ScopedAStatus::ok();
     };
 
-    ndk::ScopedAStatus isoDataReceived(const std::vector<uint8_t>& data) {
+    ndk::ScopedAStatus isoDataReceived(
+        const std::vector<uint8_t>& data) override {
       parent_.iso_cb_count++;
       parent_.iso_queue.push(data);
       return ScopedAStatus::ok();
@@ -253,7 +236,8 @@
   template <class T>
   class WaitQueue {
    public:
-    WaitQueue(){};
+    WaitQueue() = default;
+    ;
 
     virtual ~WaitQueue() = default;
 
@@ -283,6 +267,14 @@
       return true;
     };
 
+    void pop() {
+      std::lock_guard<std::mutex> lock(m_);
+      if (q_.empty()) {
+        return;
+      }
+      q_.pop();
+    };
+
     bool front(T& v) {
       std::lock_guard<std::mutex> lock(m_);
       if (q_.empty()) {
@@ -329,22 +321,22 @@
 
   std::shared_ptr<IBluetoothHci> hci;
   std::shared_ptr<BluetoothHciCallbacks> hci_cb;
-  AIBinder_DeathRecipient* bluetooth_hci_death_recipient;
+  AIBinder_DeathRecipient* bluetooth_hci_death_recipient{};
   WaitQueue<std::vector<uint8_t>> event_queue;
   WaitQueue<std::vector<uint8_t>> acl_queue;
   WaitQueue<std::vector<uint8_t>> sco_queue;
   WaitQueue<std::vector<uint8_t>> iso_queue;
 
   std::promise<bool> initialized_promise;
-  int event_cb_count;
-  int sco_cb_count;
-  int acl_cb_count;
-  int iso_cb_count;
+  int event_cb_count{};
+  int sco_cb_count{};
+  int acl_cb_count{};
+  int iso_cb_count{};
 
-  int max_acl_data_packet_length;
-  int max_sco_data_packet_length;
-  int max_acl_data_packets;
-  int max_sco_data_packets;
+  int max_acl_data_packet_length{};
+  int max_sco_data_packet_length{};
+  int max_acl_data_packets{};
+  int max_sco_data_packets{};
 
   std::vector<uint16_t> sco_connection_handles;
   std::vector<uint16_t> acl_connection_handles;
@@ -355,17 +347,20 @@
   while (!event_queue.empty()) {
     std::vector<uint8_t> event;
     event_queue.front(event);
-    ASSERT_GE(event.size(),
-              static_cast<size_t>(kEventCommandCompleteStatusByte));
-    bool event_is_no_op =
-        (event[kEventCodeByte] == kEventCommandComplete) &&
-        (event[kEventCommandCompleteOpcodeLsByte] == 0x00) &&
-        (event[kEventCommandCompleteOpcodeLsByte + 1] == 0x00);
-    event_is_no_op |= (event[kEventCodeByte] == kEventCommandStatus) &&
-                      (event[kEventCommandStatusOpcodeLsByte] == 0x00) &&
-                      (event[kEventCommandStatusOpcodeLsByte + 1] == 0x00);
-    if (event_is_no_op) {
-      event_queue.pop(event);
+    auto complete_view = ::bluetooth::hci::CommandCompleteView::Create(
+        ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
+            std::make_shared<std::vector<uint8_t>>(event))));
+    auto status_view = ::bluetooth::hci::CommandCompleteView::Create(
+        ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
+            std::make_shared<std::vector<uint8_t>>(event))));
+    bool is_complete_no_op =
+        complete_view.IsValid() &&
+        complete_view.GetCommandOpCode() == ::bluetooth::hci::OpCode::NONE;
+    bool is_status_no_op =
+        status_view.IsValid() &&
+        status_view.GetCommandOpCode() == ::bluetooth::hci::OpCode::NONE;
+    if (is_complete_no_op || is_status_no_op) {
+      event_queue.pop();
     } else {
       break;
     }
@@ -377,12 +372,12 @@
   while (!acl_queue.empty()) {
     std::vector<uint8_t> acl_packet;
     acl_queue.front(acl_packet);
-    uint16_t connection_handle = acl_packet[1] & 0xF;
-    connection_handle <<= 8;
-    connection_handle |= acl_packet[0];
-    bool packet_is_no_op = connection_handle == kAclHandleQcaDebugMessage;
-    if (packet_is_no_op) {
-      acl_queue.pop(acl_packet);
+    auto acl_view =
+        ::bluetooth::hci::AclView::Create(::bluetooth::hci::PacketView<true>(
+            std::make_shared<std::vector<uint8_t>>(acl_packet)));
+    EXPECT_TRUE(acl_view.IsValid());
+    if (acl_view.GetHandle() == kAclHandleQcaDebugMessage) {
+      acl_queue.pop();
     } else {
       break;
     }
@@ -407,48 +402,49 @@
   }
 }
 
-// Wait until a command complete is received.
 void BluetoothAidlTest::wait_for_command_complete_event(
-    std::vector<uint8_t> cmd) {
+    OpCode opCode, std::vector<uint8_t>& complete_event) {
   ASSERT_NO_FATAL_FAILURE(wait_for_event());
-  std::vector<uint8_t> event;
   ASSERT_FALSE(event_queue.empty());
-  ASSERT_TRUE(event_queue.pop(event));
+  ASSERT_TRUE(event_queue.pop(complete_event));
+  auto complete_view = ::bluetooth::hci::CommandCompleteView::Create(
+      ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
+          std::make_shared<std::vector<uint8_t>>(complete_event))));
+  ASSERT_TRUE(complete_view.IsValid());
+  ASSERT_EQ(complete_view.GetCommandOpCode(), opCode);
+  ASSERT_EQ(complete_view.GetPayload()[0],
+            static_cast<uint8_t>(::bluetooth::hci::ErrorCode::SUCCESS));
+}
 
-  ASSERT_GT(event.size(), static_cast<size_t>(kEventCommandCompleteStatusByte));
-  ASSERT_EQ(kEventCommandComplete, event[kEventCodeByte]);
-  ASSERT_EQ(cmd[0], event[kEventCommandCompleteOpcodeLsByte]);
-  ASSERT_EQ(cmd[1], event[kEventCommandCompleteOpcodeLsByte + 1]);
-  ASSERT_EQ(kHciStatusSuccess, event[kEventCommandCompleteStatusByte]);
+void BluetoothAidlTest::wait_and_validate_command_complete_event(
+    ::bluetooth::hci::OpCode opCode) {
+  std::vector<uint8_t> complete_event;
+  ASSERT_NO_FATAL_FAILURE(
+      wait_for_command_complete_event(opCode, complete_event));
 }
 
 // Send the command to read the controller's buffer sizes.
 void BluetoothAidlTest::setBufferSizes() {
-  std::vector<uint8_t> cmd{
-      kCommandHciReadBufferSize,
-      kCommandHciReadBufferSize + sizeof(kCommandHciReadBufferSize)};
+  std::vector<uint8_t> cmd;
+  ::bluetooth::packet::BitInserter bi{cmd};
+  ::bluetooth::hci::ReadBufferSizeBuilder::Create()->Serialize(bi);
   hci->sendHciCommand(cmd);
 
   ASSERT_NO_FATAL_FAILURE(wait_for_event());
-  if (event_queue.empty()) {
-    return;
-  }
   std::vector<uint8_t> event;
   ASSERT_TRUE(event_queue.pop(event));
+  auto complete_view = ::bluetooth::hci::ReadBufferSizeCompleteView::Create(
+      ::bluetooth::hci::CommandCompleteView::Create(
+          ::bluetooth::hci::EventView::Create(
+              ::bluetooth::hci::PacketView<true>(
+                  std::make_shared<std::vector<uint8_t>>(event)))));
 
-  ASSERT_EQ(kEventCommandComplete, event[kEventCodeByte]);
-  ASSERT_EQ(cmd[0], event[kEventCommandCompleteOpcodeLsByte]);
-  ASSERT_EQ(cmd[1], event[kEventCommandCompleteOpcodeLsByte + 1]);
-  ASSERT_EQ(kHciStatusSuccess, event[kEventCommandCompleteStatusByte]);
-
-  max_acl_data_packet_length =
-      event[kEventCommandCompleteStatusByte + 1] +
-      (event[kEventCommandCompleteStatusByte + 2] << 8);
-  max_sco_data_packet_length = event[kEventCommandCompleteStatusByte + 3];
-  max_acl_data_packets = event[kEventCommandCompleteStatusByte + 4] +
-                         (event[kEventCommandCompleteStatusByte + 5] << 8);
-  max_sco_data_packets = event[kEventCommandCompleteStatusByte + 6] +
-                         (event[kEventCommandCompleteStatusByte + 7] << 8);
+  ASSERT_TRUE(complete_view.IsValid());
+  ASSERT_EQ(complete_view.GetStatus(), ::bluetooth::hci::ErrorCode::SUCCESS);
+  max_acl_data_packet_length = complete_view.GetAclDataPacketLength();
+  max_sco_data_packet_length = complete_view.GetSynchronousDataPacketLength();
+  max_acl_data_packets = complete_view.GetTotalNumAclDataPackets();
+  max_sco_data_packets = complete_view.GetTotalNumSynchronousDataPackets();
 
   ALOGD("%s: ACL max %d num %d SCO max %d num %d", __func__,
         static_cast<int>(max_acl_data_packet_length),
@@ -459,39 +455,39 @@
 
 // Enable flow control packets for SCO
 void BluetoothAidlTest::setSynchronousFlowControlEnable() {
-  std::vector<uint8_t> cmd{kCommandHciSynchronousFlowControlEnable,
-                           kCommandHciSynchronousFlowControlEnable +
-                               sizeof(kCommandHciSynchronousFlowControlEnable)};
+  std::vector<uint8_t> cmd;
+  ::bluetooth::packet::BitInserter bi{cmd};
+  ::bluetooth::hci::WriteSynchronousFlowControlEnableBuilder::Create(
+      ::bluetooth::hci::Enable::ENABLED)
+      ->Serialize(bi);
   hci->sendHciCommand(cmd);
 
-  wait_for_command_complete_event(cmd);
+  wait_and_validate_command_complete_event(
+      ::bluetooth::hci::OpCode::WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE);
 }
 
 // Send an HCI command (in Loopback mode) and check the response.
 void BluetoothAidlTest::sendAndCheckHci(int num_packets) {
-  ThroughputLogger logger = {__func__};
-  int command_size = 0;
+  ThroughputLogger logger{__func__};
+  size_t command_size = 0;
+  char new_name[] = "John Jacob Jingleheimer Schmidt ___________________";
+  size_t new_name_length = strlen(new_name);
   for (int n = 0; n < num_packets; n++) {
-    // Send an HCI packet
-    std::vector<uint8_t> write_name{
-        kCommandHciWriteLocalName,
-        kCommandHciWriteLocalName + sizeof(kCommandHciWriteLocalName)};
-    // With a name
-    char new_name[] = "John Jacob Jingleheimer Schmidt ___________________0";
-    size_t new_name_length = strlen(new_name);
+    // The name to set is new_name
+    std::array<uint8_t, 248> name_array{};
     for (size_t i = 0; i < new_name_length; i++) {
-      write_name.push_back(static_cast<uint8_t>(new_name[i]));
+      name_array[i] = new_name[i];
     }
     // And the packet number
-    size_t i = new_name_length - 1;
-    for (int digits = n; digits > 0; digits = digits / 10, i--) {
-      write_name[i] = static_cast<uint8_t>('0' + digits % 10);
+    char number[11] = "0000000000";
+    snprintf(number, sizeof(number), "%010d", static_cast<int>(n));
+    for (size_t i = new_name_length; i < new_name_length + sizeof(number) - 1;
+         i++) {
+      name_array[new_name_length + i] = number[i];
     }
-    // And padding
-    for (size_t i = 0; i < 248 - new_name_length; i++) {
-      write_name.push_back(static_cast<uint8_t>(0));
-    }
-
+    std::vector<uint8_t> write_name;
+    ::bluetooth::packet::BitInserter bi{write_name};
+    ::bluetooth::hci::WriteLocalNameBuilder::Create(name_array)->Serialize(bi);
     hci->sendHciCommand(write_name);
 
     // Check the loopback of the HCI packet
@@ -499,28 +495,17 @@
 
     std::vector<uint8_t> event;
     ASSERT_TRUE(event_queue.pop(event));
-
-    size_t compare_length = (write_name.size() > static_cast<size_t>(0xff)
-                                 ? static_cast<size_t>(0xff)
-                                 : write_name.size());
-    ASSERT_GT(event.size(), compare_length + kEventFirstPayloadByte - 1);
-
-    ASSERT_EQ(kEventLoopbackCommand, event[kEventCodeByte]);
-    ASSERT_EQ(compare_length, event[kEventLengthByte]);
-
-    // Don't compare past the end of the event.
-    if (compare_length + kEventFirstPayloadByte > event.size()) {
-      compare_length = event.size() - kEventFirstPayloadByte;
-      ALOGE("Only comparing %d bytes", static_cast<int>(compare_length));
-    }
+    auto event_view = ::bluetooth::hci::LoopbackCommandView::Create(
+        ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
+            std::make_shared<std::vector<uint8_t>>(event))));
+    ASSERT_TRUE(event_view.IsValid());
+    std::vector<uint8_t> looped_back_command{event_view.GetPayload().begin(),
+                                             event_view.GetPayload().end()};
+    ASSERT_EQ(looped_back_command, write_name);
 
     if (n == num_packets - 1) {
       command_size = write_name.size();
     }
-
-    for (size_t i = 0; i < compare_length; i++) {
-      ASSERT_EQ(write_name[i], event[kEventFirstPayloadByte + i]);
-    }
   }
   logger.setTotalBytes(command_size * num_packets * 2);
 }
@@ -528,16 +513,18 @@
 // Send a SCO data packet (in Loopback mode) and check the response.
 void BluetoothAidlTest::sendAndCheckSco(int num_packets, size_t size,
                                         uint16_t handle) {
-  ThroughputLogger logger = {__func__};
+  ThroughputLogger logger{__func__};
   for (int n = 0; n < num_packets; n++) {
     // Send a SCO packet
     std::vector<uint8_t> sco_packet;
-    sco_packet.push_back(static_cast<uint8_t>(handle & 0xff));
-    sco_packet.push_back(static_cast<uint8_t>((handle & 0x0f00) >> 8));
-    sco_packet.push_back(static_cast<uint8_t>(size & 0xff));
+    std::vector<uint8_t> payload;
     for (size_t i = 0; i < size; i++) {
-      sco_packet.push_back(static_cast<uint8_t>(i + n));
+      payload.push_back(static_cast<uint8_t>(i + n));
     }
+    ::bluetooth::packet::BitInserter bi{sco_packet};
+    ::bluetooth::hci::ScoBuilder::Create(
+        handle, ::bluetooth::hci::PacketStatusFlag::CORRECTLY_RECEIVED, payload)
+        ->Serialize(bi);
     hci->sendScoData(sco_packet);
 
     // Check the loopback of the SCO packet
@@ -545,21 +532,7 @@
     ASSERT_TRUE(
         sco_queue.tryPopWithTimeout(sco_loopback, kWaitForScoDataTimeout));
 
-    ASSERT_EQ(sco_packet.size(), sco_loopback.size());
-    size_t successful_bytes = 0;
-
-    for (size_t i = 0; i < sco_packet.size(); i++) {
-      if (sco_packet[i] == sco_loopback[i]) {
-        successful_bytes = i;
-      } else {
-        ALOGE("Miscompare at %d (expected %x, got %x)", static_cast<int>(i),
-              sco_packet[i], sco_loopback[i]);
-        ALOGE("At %d (expected %x, got %x)", static_cast<int>(i + 1),
-              sco_packet[i + 1], sco_loopback[i + 1]);
-        break;
-      }
-    }
-    ASSERT_EQ(sco_packet.size(), successful_bytes + 1);
+    ASSERT_EQ(sco_packet, sco_loopback);
   }
   logger.setTotalBytes(num_packets * size * 2);
 }
@@ -567,19 +540,20 @@
 // Send an ACL data packet (in Loopback mode) and check the response.
 void BluetoothAidlTest::sendAndCheckAcl(int num_packets, size_t size,
                                         uint16_t handle) {
-  ThroughputLogger logger = {__func__};
+  ThroughputLogger logger{__func__};
   for (int n = 0; n < num_packets; n++) {
-    // Send an ACL packet
-    std::vector<uint8_t> acl_packet;
-    acl_packet.push_back(static_cast<uint8_t>(handle & 0xff));
-    acl_packet.push_back(static_cast<uint8_t>((handle & 0x0f00) >> 8) |
-                         kAclBroadcastPointToPoint |
-                         kAclPacketBoundaryFirstAutoFlushable);
-    acl_packet.push_back(static_cast<uint8_t>(size & 0xff));
-    acl_packet.push_back(static_cast<uint8_t>((size & 0xff00) >> 8));
+    // Send an ACL packet with counting data
+    auto payload = std::make_unique<::bluetooth::packet::RawBuilder>();
     for (size_t i = 0; i < size; i++) {
-      acl_packet.push_back(static_cast<uint8_t>(i + n));
+      payload->AddOctets1(static_cast<uint8_t>(i + n));
     }
+    std::vector<uint8_t> acl_packet;
+    ::bluetooth::packet::BitInserter bi{acl_packet};
+    ::bluetooth::hci::AclBuilder::Create(
+        handle,
+        ::bluetooth::hci::PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE,
+        ::bluetooth::hci::BroadcastFlag::POINT_TO_POINT, std::move(payload))
+        ->Serialize(bi);
     hci->sendAclData(acl_packet);
 
     std::vector<uint8_t> acl_loopback;
@@ -587,21 +561,7 @@
     ASSERT_TRUE(
         acl_queue.tryPopWithTimeout(acl_loopback, kWaitForAclDataTimeout));
 
-    ASSERT_EQ(acl_packet.size(), acl_loopback.size());
-    size_t successful_bytes = 0;
-
-    for (size_t i = 0; i < acl_packet.size(); i++) {
-      if (acl_packet[i] == acl_loopback[i]) {
-        successful_bytes = i;
-      } else {
-        ALOGE("Miscompare at %d (expected %x, got %x)", static_cast<int>(i),
-              acl_packet[i], acl_loopback[i]);
-        ALOGE("At %d (expected %x, got %x)", static_cast<int>(i + 1),
-              acl_packet[i + 1], acl_loopback[i + 1]);
-        break;
-      }
-    }
-    ASSERT_EQ(acl_packet.size(), successful_bytes + 1);
+    ASSERT_EQ(acl_packet, acl_loopback);
   }
   logger.setTotalBytes(num_packets * size * 2);
 }
@@ -620,23 +580,29 @@
     }
     std::vector<uint8_t> event;
     EXPECT_TRUE(event_queue.pop(event));
-
-    EXPECT_EQ(kEventNumberOfCompletedPackets, event[kEventCodeByte]);
-    EXPECT_EQ(1, event[kEventNumberOfCompletedPacketsNumHandles]);
-
-    uint16_t event_handle = event[3] + (event[4] << 8);
-    EXPECT_EQ(handle, event_handle);
-
-    packets_processed += event[5] + (event[6] << 8);
+    auto event_view = ::bluetooth::hci::NumberOfCompletedPacketsView::Create(
+        ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
+            std::make_shared<std::vector<uint8_t>>(event))));
+    if (!event_view.IsValid()) {
+      ADD_FAILURE();
+      return packets_processed;
+    }
+    auto completed_packets = event_view.GetCompletedPackets();
+    for (const auto& entry : completed_packets) {
+      EXPECT_EQ(handle, entry.connection_handle_);
+      packets_processed += entry.host_num_of_completed_packets_;
+    }
   }
   return packets_processed;
 }
 
 // Send local loopback command and initialize SCO and ACL handles.
 void BluetoothAidlTest::enterLoopbackMode() {
-  std::vector<uint8_t> cmd{kCommandHciWriteLoopbackModeLocal,
-                           kCommandHciWriteLoopbackModeLocal +
-                               sizeof(kCommandHciWriteLoopbackModeLocal)};
+  std::vector<uint8_t> cmd;
+  ::bluetooth::packet::BitInserter bi{cmd};
+  ::bluetooth::hci::WriteLoopbackModeBuilder::Create(
+      bluetooth::hci::LoopbackMode::ENABLE_LOCAL)
+      ->Serialize(bi);
   hci->sendHciCommand(cmd);
 
   // Receive connection complete events with data channels
@@ -652,97 +618,128 @@
     }
     std::vector<uint8_t> event;
     ASSERT_TRUE(event_queue.pop(event));
-    ASSERT_GT(event.size(),
-              static_cast<size_t>(kEventCommandCompleteStatusByte));
-    if (event[kEventCodeByte] == kEventConnectionComplete) {
-      ASSERT_GT(event.size(),
-                static_cast<size_t>(kEventConnectionCompleteType));
-      ASSERT_EQ(event[kEventLengthByte], kEventConnectionCompleteParamLength);
-      uint8_t connection_type = event[kEventConnectionCompleteType];
+    auto event_view =
+        ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
+            std::make_shared<std::vector<uint8_t>>(event)));
+    ASSERT_TRUE(event_view.IsValid());
 
-      ASSERT_TRUE(connection_type == kEventConnectionCompleteTypeSco ||
-                  connection_type == kEventConnectionCompleteTypeAcl);
-
-      // Save handles
-      uint16_t handle = event[kEventConnectionCompleteHandleLsByte] |
-                        event[kEventConnectionCompleteHandleLsByte + 1] << 8;
-      if (connection_type == kEventConnectionCompleteTypeSco) {
-        sco_connection_handles.push_back(handle);
-      } else {
-        acl_connection_handles.push_back(handle);
+    if (event_view.GetEventCode() ==
+        ::bluetooth::hci::EventCode::CONNECTION_COMPLETE) {
+      auto complete_view =
+          ::bluetooth::hci::ConnectionCompleteView::Create(event_view);
+      ASSERT_TRUE(complete_view.IsValid());
+      switch (complete_view.GetLinkType()) {
+        case ::bluetooth::hci::LinkType::ACL:
+          acl_connection_handles.push_back(complete_view.GetConnectionHandle());
+          break;
+        case ::bluetooth::hci::LinkType::SCO:
+          sco_connection_handles.push_back(complete_view.GetConnectionHandle());
+          break;
+        default:
+          ASSERT_EQ(complete_view.GetLinkType(),
+                    ::bluetooth::hci::LinkType::ACL);
       }
-
-      ALOGD("Connect complete type = %d handle = %d",
-            event[kEventConnectionCompleteType], handle);
       connection_event_count++;
     } else {
-      ASSERT_EQ(kEventCommandComplete, event[kEventCodeByte]);
-      ASSERT_EQ(cmd[0], event[kEventCommandCompleteOpcodeLsByte]);
-      ASSERT_EQ(cmd[1], event[kEventCommandCompleteOpcodeLsByte + 1]);
-      ASSERT_EQ(kHciStatusSuccess, event[kEventCommandCompleteStatusByte]);
+      auto command_complete_view =
+          ::bluetooth::hci::WriteLoopbackModeCompleteView::Create(
+              ::bluetooth::hci::CommandCompleteView::Create(event_view));
+      ASSERT_TRUE(command_complete_view.IsValid());
+      ASSERT_EQ(::bluetooth::hci::ErrorCode::SUCCESS,
+                command_complete_view.GetStatus());
       command_complete_received = true;
     }
   }
 }
 
+void BluetoothAidlTest::send_and_wait_for_cmd_complete(
+    std::unique_ptr<CommandBuilder> cmd, std::vector<uint8_t>& cmd_complete) {
+  std::vector<uint8_t> cmd_bytes = cmd->SerializeToBytes();
+  hci->sendHciCommand(cmd_bytes);
+
+  auto view = CommandView::Create(
+      PacketView<true>(std::make_shared<std::vector<uint8_t>>(cmd_bytes)));
+  ASSERT_TRUE(view.IsValid());
+  ALOGI("Waiting for %s[0x%x]", OpCodeText(view.GetOpCode()).c_str(),
+        static_cast<int>(view.GetOpCode()));
+  ASSERT_NO_FATAL_FAILURE(
+      wait_for_command_complete_event(view.GetOpCode(), cmd_complete));
+}
+
 // Empty test: Initialize()/Close() are called in SetUp()/TearDown().
 TEST_P(BluetoothAidlTest, InitializeAndClose) {}
 
 // Send an HCI Reset with sendHciCommand and wait for a command complete event.
 TEST_P(BluetoothAidlTest, HciReset) {
-  std::vector<uint8_t> reset{kCommandHciReset,
-                             kCommandHciReset + sizeof(kCommandHciReset)};
+  std::vector<uint8_t> reset;
+  ::bluetooth::packet::BitInserter bi{reset};
+  ::bluetooth::hci::ResetBuilder::Create()->Serialize(bi);
   hci->sendHciCommand(reset);
 
-  wait_for_command_complete_event(reset);
+  wait_and_validate_command_complete_event(::bluetooth::hci::OpCode::RESET);
 }
 
 // Read and check the HCI version of the controller.
 TEST_P(BluetoothAidlTest, HciVersionTest) {
-  std::vector<uint8_t> cmd{kCommandHciReadLocalVersionInformation,
-                           kCommandHciReadLocalVersionInformation +
-                               sizeof(kCommandHciReadLocalVersionInformation)};
+  std::vector<uint8_t> cmd;
+  ::bluetooth::packet::BitInserter bi{cmd};
+  ::bluetooth::hci::ReadLocalVersionInformationBuilder::Create()->Serialize(bi);
   hci->sendHciCommand(cmd);
 
   ASSERT_NO_FATAL_FAILURE(wait_for_event());
 
   std::vector<uint8_t> event;
   ASSERT_TRUE(event_queue.pop(event));
-  ASSERT_GT(event.size(), static_cast<size_t>(kEventLocalLmpVersionByte));
-
-  ASSERT_EQ(kEventCommandComplete, event[kEventCodeByte]);
-  ASSERT_EQ(cmd[0], event[kEventCommandCompleteOpcodeLsByte]);
-  ASSERT_EQ(cmd[1], event[kEventCommandCompleteOpcodeLsByte + 1]);
-  ASSERT_EQ(kHciStatusSuccess, event[kEventCommandCompleteStatusByte]);
-
-  ASSERT_LE(kHciMinimumHciVersion, event[kEventLocalHciVersionByte]);
-  ASSERT_LE(kHciMinimumLmpVersion, event[kEventLocalLmpVersionByte]);
+  auto complete_view =
+      ::bluetooth::hci::ReadLocalVersionInformationCompleteView::Create(
+          ::bluetooth::hci::CommandCompleteView::Create(
+              ::bluetooth::hci::EventView::Create(
+                  ::bluetooth::hci::PacketView<true>(
+                      std::make_shared<std::vector<uint8_t>>(event)))));
+  ASSERT_TRUE(complete_view.IsValid());
+  ASSERT_EQ(::bluetooth::hci::ErrorCode::SUCCESS, complete_view.GetStatus());
+  auto version = complete_view.GetLocalVersionInformation();
+  ASSERT_LE(::bluetooth::hci::HciVersion::V_3_0, version.hci_version_);
+  ASSERT_LE(::bluetooth::hci::LmpVersion::V_3_0, version.lmp_version_);
 }
 
 // Send an unknown HCI command and wait for the error message.
 TEST_P(BluetoothAidlTest, HciUnknownCommand) {
-  std::vector<uint8_t> cmd{
-      kCommandHciShouldBeUnknown,
-      kCommandHciShouldBeUnknown + sizeof(kCommandHciShouldBeUnknown)};
+  std::vector<uint8_t> cmd;
+  ::bluetooth::packet::BitInserter bi{cmd};
+  ::bluetooth::hci::CommandBuilder::Create(
+      static_cast<::bluetooth::hci::OpCode>(0x3cff),
+      std::make_unique<::bluetooth::packet::RawBuilder>())
+      ->Serialize(bi);
   hci->sendHciCommand(cmd);
 
   ASSERT_NO_FATAL_FAILURE(wait_for_event());
 
   std::vector<uint8_t> event;
   ASSERT_TRUE(event_queue.pop(event));
+  auto event_view =
+      ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
+          std::make_shared<std::vector<uint8_t>>(event)));
+  ASSERT_TRUE(event_view.IsValid());
 
-  ASSERT_GT(event.size(), static_cast<size_t>(kEventCommandCompleteStatusByte));
-  if (event[kEventCodeByte] == kEventCommandComplete) {
-    ASSERT_EQ(cmd[0], event[kEventCommandCompleteOpcodeLsByte]);
-    ASSERT_EQ(cmd[1], event[kEventCommandCompleteOpcodeLsByte + 1]);
-    ASSERT_EQ(kHciStatusUnknownHciCommand,
-              event[kEventCommandCompleteStatusByte]);
-  } else {
-    ASSERT_EQ(kEventCommandStatus, event[kEventCodeByte]);
-    ASSERT_EQ(cmd[0], event[kEventCommandStatusOpcodeLsByte]);
-    ASSERT_EQ(cmd[1], event[kEventCommandStatusOpcodeLsByte + 1]);
-    ASSERT_EQ(kHciStatusUnknownHciCommand,
-              event[kEventCommandStatusStatusByte]);
+  switch (event_view.GetEventCode()) {
+    case ::bluetooth::hci::EventCode::COMMAND_COMPLETE: {
+      auto command_complete =
+          ::bluetooth::hci::CommandCompleteView::Create(event_view);
+      ASSERT_TRUE(command_complete.IsValid());
+      ASSERT_EQ(command_complete.GetPayload()[0],
+                static_cast<uint8_t>(
+                    ::bluetooth::hci::ErrorCode::UNKNOWN_HCI_COMMAND));
+    } break;
+    case ::bluetooth::hci::EventCode::COMMAND_STATUS: {
+      auto command_status =
+          ::bluetooth::hci::CommandStatusView::Create(event_view);
+      ASSERT_TRUE(command_status.IsValid());
+      ASSERT_EQ(command_status.GetStatus(),
+                ::bluetooth::hci::ErrorCode::UNKNOWN_HCI_COMMAND);
+    } break;
+    default:
+      ADD_FAILURE();
   }
 }
 
@@ -851,20 +848,24 @@
 
 // Set all bits in the event mask
 TEST_P(BluetoothAidlTest, SetEventMask) {
-  std::vector<uint8_t> set_event_mask{
-      0x01, 0x0c, 0x08 /*parameter bytes*/, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-      0xff, 0xff};
-  hci->sendHciCommand({set_event_mask});
-  wait_for_command_complete_event(set_event_mask);
+  std::vector<uint8_t> cmd;
+  ::bluetooth::packet::BitInserter bi{cmd};
+  uint64_t full_mask = UINT64_MAX;
+  ::bluetooth::hci::SetEventMaskBuilder::Create(full_mask)->Serialize(bi);
+  hci->sendHciCommand(cmd);
+  wait_and_validate_command_complete_event(
+      ::bluetooth::hci::OpCode::SET_EVENT_MASK);
 }
 
 // Set all bits in the LE event mask
 TEST_P(BluetoothAidlTest, SetLeEventMask) {
-  std::vector<uint8_t> set_event_mask{
-      0x20, 0x0c, 0x08 /*parameter bytes*/, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-      0xff, 0xff};
-  hci->sendHciCommand({set_event_mask});
-  wait_for_command_complete_event(set_event_mask);
+  std::vector<uint8_t> cmd;
+  ::bluetooth::packet::BitInserter bi{cmd};
+  uint64_t full_mask = UINT64_MAX;
+  ::bluetooth::hci::LeSetEventMaskBuilder::Create(full_mask)->Serialize(bi);
+  hci->sendHciCommand(cmd);
+  wait_and_validate_command_complete_event(
+      ::bluetooth::hci::OpCode::LE_SET_EVENT_MASK);
 }
 
 // Call initialize twice, second one should fail.
@@ -872,28 +873,32 @@
   class SecondCb
       : public aidl::android::hardware::bluetooth::BnBluetoothHciCallbacks {
    public:
-    ndk::ScopedAStatus initializationComplete(Status status) {
+    ndk::ScopedAStatus initializationComplete(Status status) override {
       EXPECT_EQ(status, Status::ALREADY_INITIALIZED);
       init_promise.set_value();
       return ScopedAStatus::ok();
     };
 
-    ndk::ScopedAStatus hciEventReceived(const std::vector<uint8_t>& /*event*/) {
+    ndk::ScopedAStatus hciEventReceived(
+        const std::vector<uint8_t>& /*event*/) override {
       ADD_FAILURE();
       return ScopedAStatus::ok();
     };
 
-    ndk::ScopedAStatus aclDataReceived(const std::vector<uint8_t>& /*data*/) {
+    ndk::ScopedAStatus aclDataReceived(
+        const std::vector<uint8_t>& /*data*/) override {
       ADD_FAILURE();
       return ScopedAStatus::ok();
     };
 
-    ndk::ScopedAStatus scoDataReceived(const std::vector<uint8_t>& /*data*/) {
+    ndk::ScopedAStatus scoDataReceived(
+        const std::vector<uint8_t>& /*data*/) override {
       ADD_FAILURE();
       return ScopedAStatus::ok();
     };
 
-    ndk::ScopedAStatus isoDataReceived(const std::vector<uint8_t>& /*data*/) {
+    ndk::ScopedAStatus isoDataReceived(
+        const std::vector<uint8_t>& /*data*/) override {
       ADD_FAILURE();
       return ScopedAStatus::ok();
     };
@@ -909,6 +914,66 @@
   ASSERT_EQ(status, std::future_status::ready);
 }
 
+TEST_P(BluetoothAidlTest, Cdd_C_12_1_Bluetooth5Requirements) {
+  std::vector<uint8_t> version_event;
+  send_and_wait_for_cmd_complete(ReadLocalVersionInformationBuilder::Create(),
+                                 version_event);
+  auto version_view = ReadLocalVersionInformationCompleteView::Create(
+      CommandCompleteView::Create(EventView::Create(PacketView<true>(
+          std::make_shared<std::vector<uint8_t>>(version_event)))));
+  ASSERT_TRUE(version_view.IsValid());
+  ASSERT_EQ(::bluetooth::hci::ErrorCode::SUCCESS, version_view.GetStatus());
+  auto version = version_view.GetLocalVersionInformation();
+  if (version.hci_version_ < ::bluetooth::hci::HciVersion::V_5_0) {
+    // This test does not apply to controllers below 5.0
+    return;
+  };
+  // When HCI version is 5.0, LMP version must also be at least 5.0
+  ASSERT_GE(static_cast<int>(version.lmp_version_),
+            static_cast<int>(version.hci_version_));
+
+  std::vector<uint8_t> le_features_event;
+  send_and_wait_for_cmd_complete(LeReadLocalSupportedFeaturesBuilder::Create(),
+                                 le_features_event);
+  auto le_features_view = LeReadLocalSupportedFeaturesCompleteView::Create(
+      CommandCompleteView::Create(EventView::Create(PacketView<true>(
+          std::make_shared<std::vector<uint8_t>>(le_features_event)))));
+  ASSERT_TRUE(le_features_view.IsValid());
+  ASSERT_EQ(::bluetooth::hci::ErrorCode::SUCCESS, le_features_view.GetStatus());
+  auto le_features = le_features_view.GetLeFeatures();
+  ASSERT_TRUE(le_features & static_cast<uint64_t>(LLFeaturesBits::LL_PRIVACY));
+  ASSERT_TRUE(le_features & static_cast<uint64_t>(LLFeaturesBits::LE_2M_PHY));
+  ASSERT_TRUE(le_features &
+              static_cast<uint64_t>(LLFeaturesBits::LE_CODED_PHY));
+  ASSERT_TRUE(le_features &
+              static_cast<uint64_t>(LLFeaturesBits::LE_EXTENDED_ADVERTISING));
+
+  std::vector<uint8_t> num_adv_set_event;
+  send_and_wait_for_cmd_complete(
+      LeReadNumberOfSupportedAdvertisingSetsBuilder::Create(),
+      num_adv_set_event);
+  auto num_adv_set_view =
+      LeReadNumberOfSupportedAdvertisingSetsCompleteView::Create(
+          CommandCompleteView::Create(EventView::Create(PacketView<true>(
+              std::make_shared<std::vector<uint8_t>>(num_adv_set_event)))));
+  ASSERT_TRUE(num_adv_set_view.IsValid());
+  ASSERT_EQ(::bluetooth::hci::ErrorCode::SUCCESS, num_adv_set_view.GetStatus());
+  auto num_adv_set = num_adv_set_view.GetNumberSupportedAdvertisingSets();
+  ASSERT_GE(num_adv_set, kMinLeAdvSetForBt5);
+
+  std::vector<uint8_t> num_resolving_list_event;
+  send_and_wait_for_cmd_complete(LeReadResolvingListSizeBuilder::Create(),
+                                 num_resolving_list_event);
+  auto num_resolving_list_view = LeReadResolvingListSizeCompleteView::Create(
+      CommandCompleteView::Create(EventView::Create(PacketView<true>(
+          std::make_shared<std::vector<uint8_t>>(num_resolving_list_event)))));
+  ASSERT_TRUE(num_resolving_list_view.IsValid());
+  ASSERT_EQ(::bluetooth::hci::ErrorCode::SUCCESS,
+            num_resolving_list_view.GetStatus());
+  auto num_resolving_list = num_resolving_list_view.GetResolvingListSize();
+  ASSERT_GE(num_resolving_list, kMinLeResolvingListForBt5);
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BluetoothAidlTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, BluetoothAidlTest,
                          testing::ValuesIn(android::getAidlHalInstanceNames(
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/AmFmRegionConfig.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/AmFmRegionConfig.aidl
index a3086c6..f4c6fd3 100644
--- a/broadcastradio/aidl/android/hardware/broadcastradio/AmFmRegionConfig.aidl
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/AmFmRegionConfig.aidl
@@ -73,14 +73,15 @@
     /**
      * De-emphasis filter supported/configured.
      *
-     * It is a bitset of de-emphasis values (DEEMPHASIS_D50 and DEEMPHASIS_D75).
+     * It can be a combination of de-emphasis values ({@link #DEEMPHASIS_D50} and
+     * {@link #DEEMPHASIS_D75}).
      */
     int fmDeemphasis;
 
     /**
      * RDS/RBDS variant supported/configured.
      *
-     * It is a bitset of RDS values (RDS and RBDS).
+     * It can be a combination of RDS values ({@link #RDS} and {@link #RBDS}).
      */
     int fmRds;
 }
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl
index d650239..7632c81 100644
--- a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl
@@ -150,6 +150,10 @@
 
     /**
      * Program flags.
+     *
+     * It can be a combination of {@link #FLAG_LIVE}, {@link #FLAG_MUTED},
+     * {@link #FLAG_TRAFFIC_PROGRAM}, {@link #FLAG_TRAFFIC_ANNOUNCEMENT},
+     * {@link #FLAG_TUNABLE}, and {@link #FLAG_STEREO}.
      */
     int infoFlags;
 
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramSelector.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramSelector.aidl
index 93b0e12..71b824b 100644
--- a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramSelector.aidl
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramSelector.aidl
@@ -51,8 +51,7 @@
      *  - analogue AM/FM: AMFM_FREQUENCY_KHZ;
      *  - FM RDS: RDS_PI;
      *  - HD Radio: HD_STATION_ID_EXT;
-     *  - DAB/DMB: DAB_SID_EXT (when used, DAB_ENSEMBLE and DAB_FREQUENCY_KHZ
-     *    must present in secondaryIds);
+     *  - DAB/DMB: DAB_SID_EXT;
      *  - Digital Radio Mondiale: DRMO_SERVICE_ID;
      *  - SiriusXM: SXM_SERVICE_ID;
      *  - vendor-specific: VENDOR_START..VENDOR_END.
@@ -63,13 +62,16 @@
      * Secondary program identifiers.
      *
      * These identifiers are supplementary and can speed up tuning process,
-     * but the primary ID must be sufficient (i.e. RDS PI is enough to select
+     * but the primary ID should be sufficient (i.e. RDS PI is enough to select
      * a station from the list after a full band scan).
      *
      * Two selectors with different secondary IDs, but the same primary ID are
      * considered equal. In particular, secondary IDs array may get updated for
      * an entry on the program list (ie. when a better frequency for a given
      * station is found).
+     *
+     * If DAB_SID_EXT is used as primaryId, using DAB_ENSEMBLE or DAB_FREQUENCY_KHZ
+     * as secondray identifiers can uniquely identify the DAB station.
      */
     ProgramIdentifier[] secondaryIds;
 }
diff --git a/broadcastradio/aidl/default/BroadcastRadio.cpp b/broadcastradio/aidl/default/BroadcastRadio.cpp
index 36520fb..c0c475a 100644
--- a/broadcastradio/aidl/default/BroadcastRadio.cpp
+++ b/broadcastradio/aidl/default/BroadcastRadio.cpp
@@ -589,10 +589,11 @@
     }
     ProgramSelector sel = {};
     if (isDab) {
-        if (numArgs != 5) {
+        if (numArgs != 5 && numArgs != 3) {
             dprintf(fd,
                     "Invalid number of arguments: please provide "
-                    "--tune dab <SID> <ENSEMBLE> <FREQUENCY>\n");
+                    "--tune dab <SID> <ENSEMBLE> <FREQUENCY> or "
+                    "--tune dab <SID>\n");
             return STATUS_BAD_VALUE;
         }
         int sid;
@@ -600,17 +601,21 @@
             dprintf(fd, "Non-integer sid provided with tune: %s\n", args[2]);
             return STATUS_BAD_VALUE;
         }
-        int ensemble;
-        if (!utils::parseArgInt(string(args[3]), &ensemble)) {
-            dprintf(fd, "Non-integer ensemble provided with tune: %s\n", args[3]);
-            return STATUS_BAD_VALUE;
+        if (numArgs == 3) {
+            sel = utils::makeSelectorDab(sid);
+        } else {
+            int ensemble;
+            if (!utils::parseArgInt(string(args[3]), &ensemble)) {
+                dprintf(fd, "Non-integer ensemble provided with tune: %s\n", args[3]);
+                return STATUS_BAD_VALUE;
+            }
+            int freq;
+            if (!utils::parseArgInt(string(args[4]), &freq)) {
+                dprintf(fd, "Non-integer frequency provided with tune: %s\n", args[4]);
+                return STATUS_BAD_VALUE;
+            }
+            sel = utils::makeSelectorDab(sid, ensemble, freq);
         }
-        int freq;
-        if (!utils::parseArgInt(string(args[4]), &freq)) {
-            dprintf(fd, "Non-integer frequency provided with tune: %s\n", args[4]);
-            return STATUS_BAD_VALUE;
-        }
-        sel = utils::makeSelectorDab(sid, ensemble, freq);
     } else {
         if (numArgs != 3) {
             dprintf(fd, "Invalid number of arguments: please provide --tune amfm <FREQUENCY>\n");
diff --git a/broadcastradio/common/utilsaidl/Utils.cpp b/broadcastradio/common/utilsaidl/Utils.cpp
index ad82366..0551bad 100644
--- a/broadcastradio/common/utilsaidl/Utils.cpp
+++ b/broadcastradio/common/utilsaidl/Utils.cpp
@@ -136,9 +136,18 @@
             return getHdSubchannel(b) == 0 &&
                    haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY_KHZ);
         case IdentifierType::DAB_SID_EXT:
-            return haveEqualIds(a, b, IdentifierType::DAB_SID_EXT) &&
-                   haveEqualIds(a, b, IdentifierType::DAB_ENSEMBLE) &&
-                   haveEqualIds(a, b, IdentifierType::DAB_FREQUENCY_KHZ);
+            if (!haveEqualIds(a, b, IdentifierType::DAB_SID_EXT)) {
+                return false;
+            }
+            if (hasId(a, IdentifierType::DAB_ENSEMBLE) &&
+                !haveEqualIds(a, b, IdentifierType::DAB_ENSEMBLE)) {
+                return false;
+            }
+            if (hasId(a, IdentifierType::DAB_FREQUENCY_KHZ) &&
+                !haveEqualIds(a, b, IdentifierType::DAB_FREQUENCY_KHZ)) {
+                return false;
+            }
+            return true;
         case IdentifierType::DRMO_SERVICE_ID:
             return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID);
         case IdentifierType::SXM_SERVICE_ID:
@@ -289,25 +298,7 @@
          sel.primaryId.type > IdentifierType::VENDOR_END)) {
         return false;
     }
-    if (!isValid(sel.primaryId)) {
-        return false;
-    }
-
-    bool isDab = sel.primaryId.type == IdentifierType::DAB_SID_EXT;
-    bool hasDabEnsemble = false;
-    bool hasDabFrequency = false;
-    for (auto it = sel.secondaryIds.begin(); it != sel.secondaryIds.end(); it++) {
-        if (!isValid(*it)) {
-            return false;
-        }
-        if (isDab && it->type == IdentifierType::DAB_ENSEMBLE) {
-            hasDabEnsemble = true;
-        }
-        if (isDab && it->type == IdentifierType::DAB_FREQUENCY_KHZ) {
-            hasDabFrequency = true;
-        }
-    }
-    return !isDab || (hasDabEnsemble && hasDabFrequency);
+    return isValid(sel.primaryId);
 }
 
 ProgramIdentifier makeIdentifier(IdentifierType type, int64_t value) {
@@ -320,6 +311,12 @@
     return sel;
 }
 
+ProgramSelector makeSelectorDab(int64_t sidExt) {
+    ProgramSelector sel = {};
+    sel.primaryId = makeIdentifier(IdentifierType::DAB_SID_EXT, sidExt);
+    return sel;
+}
+
 ProgramSelector makeSelectorDab(int64_t sidExt, int32_t ensemble, int64_t freq) {
     ProgramSelector sel = {};
     sel.primaryId = makeIdentifier(IdentifierType::DAB_SID_EXT, sidExt);
diff --git a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
index beebd03..ad075f2 100644
--- a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
+++ b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
@@ -138,6 +138,7 @@
 
 ProgramIdentifier makeIdentifier(IdentifierType type, int64_t value);
 ProgramSelector makeSelectorAmfm(int32_t frequency);
+ProgramSelector makeSelectorDab(int64_t sidExt);
 ProgramSelector makeSelectorDab(int64_t sidExt, int32_t ensemble, int64_t freq);
 
 bool satisfies(const ProgramFilter& filter, const ProgramSelector& sel);
diff --git a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
index 50fb052..a755d57 100644
--- a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
+++ b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
@@ -2025,16 +2025,7 @@
 }
 
 TEST_P(CameraAidlTest, process8BitColorSpaceRequests) {
-    static int profiles[] = {
-        ColorSpaceNamed::BT709,
-        ColorSpaceNamed::DCI_P3,
-        ColorSpaceNamed::DISPLAY_P3,
-        ColorSpaceNamed::EXTENDED_SRGB,
-        ColorSpaceNamed::LINEAR_EXTENDED_SRGB,
-        ColorSpaceNamed::NTSC_1953,
-        ColorSpaceNamed::SMPTE_C,
-        ColorSpaceNamed::SRGB
-    };
+    static int profiles[] = {ColorSpaceNamed::DISPLAY_P3, ColorSpaceNamed::SRGB};
 
     for (int32_t i = 0; i < sizeof(profiles) / sizeof(profiles[0]); i++) {
         processColorSpaceRequest(static_cast<RequestAvailableColorSpaceProfilesMap>(profiles[i]),
@@ -2059,10 +2050,10 @@
         ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM_PO
     };
 
-    // Process all dynamic range profiles with BT2020
+    // Process all dynamic range profiles with BT2020_HLG
     for (int32_t i = 0; i < sizeof(dynamicRangeProfiles) / sizeof(dynamicRangeProfiles[0]); i++) {
         processColorSpaceRequest(
-                static_cast<RequestAvailableColorSpaceProfilesMap>(ColorSpaceNamed::BT2020),
+                static_cast<RequestAvailableColorSpaceProfilesMap>(ColorSpaceNamed::BT2020_HLG),
                 static_cast<RequestAvailableDynamicRangeProfilesMap>(dynamicRangeProfiles[i]));
     }
 }
@@ -3051,6 +3042,47 @@
     configureStreamUseCaseInternal(previewStreamThreshold);
 }
 
+// Validate the integrity of stream configuration metadata
+TEST_P(CameraAidlTest, validateStreamConfigurations) {
+    std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider);
+    std::vector<AvailableStream> outputStreams;
+
+    const int32_t scalerSizesTag = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
+    const int32_t scalerMinFrameDurationsTag = ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS;
+    const int32_t scalerStallDurationsTag = ANDROID_SCALER_AVAILABLE_STALL_DURATIONS;
+
+    for (const auto& name : cameraDeviceNames) {
+        CameraMetadata meta;
+        std::shared_ptr<ICameraDevice> cameraDevice;
+
+        openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/,
+                               &cameraDevice /*out*/);
+        camera_metadata_t* staticMeta = reinterpret_cast<camera_metadata_t*>(meta.metadata.data());
+
+        if (is10BitDynamicRangeCapable(staticMeta)) {
+            std::vector<std::tuple<size_t, size_t>> supportedP010Sizes, supportedBlobSizes;
+
+            getSupportedSizes(staticMeta, scalerSizesTag, HAL_PIXEL_FORMAT_BLOB,
+                              &supportedBlobSizes);
+            getSupportedSizes(staticMeta, scalerSizesTag, HAL_PIXEL_FORMAT_YCBCR_P010,
+                              &supportedP010Sizes);
+            ASSERT_FALSE(supportedP010Sizes.empty());
+
+            std::vector<int64_t> blobMinDurations, blobStallDurations;
+            getSupportedDurations(staticMeta, scalerMinFrameDurationsTag, HAL_PIXEL_FORMAT_BLOB,
+                                  supportedP010Sizes, &blobMinDurations);
+            getSupportedDurations(staticMeta, scalerStallDurationsTag, HAL_PIXEL_FORMAT_BLOB,
+                                  supportedP010Sizes, &blobStallDurations);
+            ASSERT_FALSE(blobStallDurations.empty());
+            ASSERT_FALSE(blobMinDurations.empty());
+            ASSERT_EQ(supportedP010Sizes.size(), blobMinDurations.size());
+            ASSERT_EQ(blobMinDurations.size(), blobStallDurations.size());
+        }
+
+        // Validate other aspects of stream configuration metadata...
+    }
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CameraAidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, CameraAidlTest,
diff --git a/camera/provider/aidl/vts/camera_aidl_test.cpp b/camera/provider/aidl/vts/camera_aidl_test.cpp
index 64507fe..c315879 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.cpp
+++ b/camera/provider/aidl/vts/camera_aidl_test.cpp
@@ -3085,6 +3085,10 @@
             return "CIE_XYZ";
         case ColorSpaceNamed::CIE_LAB:
             return "CIE_LAB";
+        case ColorSpaceNamed::BT2020_HLG:
+            return "BT2020_HLG";
+        case ColorSpaceNamed::BT2020_PQ:
+            return "BT2020_PQ";
         default:
             return "INVALID";
     }
@@ -3333,7 +3337,6 @@
         RequestAvailableColorSpaceProfilesMap colorSpace,
         RequestAvailableDynamicRangeProfilesMap dynamicRangeProfile) {
     std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider);
-    int64_t bufferId = 1;
     CameraMetadata settings;
 
     for (const auto& name : cameraDeviceNames) {
@@ -3452,12 +3455,12 @@
         // Stream as long as needed to fill the Hal inflight queue
         std::vector<CaptureRequest> requests(halStreams[0].maxBuffers);
 
-        for (int32_t frameNumber = 0; frameNumber < requests.size(); frameNumber++) {
+        for (int32_t requestId = 0; requestId < requests.size(); requestId++) {
             std::shared_ptr<InFlightRequest> inflightReq = std::make_shared<InFlightRequest>(
                     static_cast<ssize_t>(halStreams.size()), false, supportsPartialResults,
                     partialResultCount, std::unordered_set<std::string>(), resultQueue);
 
-            CaptureRequest& request = requests[frameNumber];
+            CaptureRequest& request = requests[requestId];
             std::vector<StreamBuffer>& outputBuffers = request.outputBuffers;
             outputBuffers.resize(halStreams.size());
 
@@ -3466,6 +3469,7 @@
             std::vector<buffer_handle_t> graphicBuffers;
             graphicBuffers.reserve(halStreams.size());
 
+            auto bufferId = requestId + 1;  // Buffer id value 0 is not valid
             for (const auto& halStream : halStreams) {
                 buffer_handle_t buffer_handle;
                 if (useHalBufManager) {
@@ -3481,17 +3485,16 @@
 
                     inflightReq->mOutstandingBufferIds[halStream.id][bufferId] = buffer_handle;
                     graphicBuffers.push_back(buffer_handle);
-                    outputBuffers[k] = {halStream.id, bufferId,
-                        android::makeToAidl(buffer_handle), BufferStatus::OK, NativeHandle(),
-                        NativeHandle()};
-                    bufferId++;
+                    outputBuffers[k] = {
+                            halStream.id,     bufferId,       android::makeToAidl(buffer_handle),
+                            BufferStatus::OK, NativeHandle(), NativeHandle()};
                 }
                 k++;
             }
 
             request.inputBuffer = {
                     -1, 0, NativeHandle(), BufferStatus::ERROR, NativeHandle(), NativeHandle()};
-            request.frameNumber = frameNumber;
+            request.frameNumber = bufferId;
             request.fmqSettingsSize = 0;
             request.settings = settings;
             request.inputWidth = 0;
@@ -3499,9 +3502,8 @@
 
             {
                 std::unique_lock<std::mutex> l(mLock);
-                mInflightMap[frameNumber] = inflightReq;
+                mInflightMap[bufferId] = inflightReq;
             }
-
         }
 
         int32_t numRequestProcessed = 0;
@@ -3515,7 +3517,10 @@
                 std::vector<int32_t> {halStreams[0].id});
         ASSERT_TRUE(returnStatus.isOk());
 
-        for (int32_t frameNumber = 0; frameNumber < requests.size(); frameNumber++) {
+        // We are keeping frame numbers and buffer ids consistent. Buffer id value of 0
+        // is used to indicate a buffer that is not present/available so buffer ids as well
+        // as frame numbers begin with 1.
+        for (int32_t frameNumber = 1; frameNumber <= requests.size(); frameNumber++) {
             const auto& inflightReq = mInflightMap[frameNumber];
             std::unique_lock<std::mutex> l(mLock);
             while (!inflightReq->errorCodeValid &&
@@ -3703,3 +3708,48 @@
         ASSERT_TRUE(ret.isOk());
     }
 }
+
+void CameraAidlTest::getSupportedSizes(const camera_metadata_t* ch, uint32_t tag, int32_t format,
+                                       std::vector<std::tuple<size_t, size_t>>* sizes /*out*/) {
+    if (sizes == nullptr) {
+        return;
+    }
+
+    camera_metadata_ro_entry entry;
+    int retcode = find_camera_metadata_ro_entry(ch, tag, &entry);
+    if ((0 == retcode) && (entry.count > 0)) {
+        // Scaler entry contains 4 elements (format, width, height, type)
+        for (size_t i = 0; i < entry.count; i += 4) {
+            if ((entry.data.i32[i] == format) &&
+                (entry.data.i32[i + 3] == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT)) {
+                sizes->push_back(std::make_tuple(entry.data.i32[i + 1], entry.data.i32[i + 2]));
+            }
+        }
+    }
+}
+
+void CameraAidlTest::getSupportedDurations(const camera_metadata_t* ch, uint32_t tag,
+                                           int32_t format,
+                                           const std::vector<std::tuple<size_t, size_t>>& sizes,
+                                           std::vector<int64_t>* durations /*out*/) {
+    if (durations == nullptr) {
+        return;
+    }
+
+    camera_metadata_ro_entry entry;
+    int retcode = find_camera_metadata_ro_entry(ch, tag, &entry);
+    if ((0 == retcode) && (entry.count > 0)) {
+        // Duration entry contains 4 elements (format, width, height, duration)
+        for (const auto& size : sizes) {
+            int64_t width = std::get<0>(size);
+            int64_t height = std::get<1>(size);
+            for (size_t i = 0; i < entry.count; i += 4) {
+                if ((entry.data.i64[i] == format) && (entry.data.i64[i + 1] == width) &&
+                    (entry.data.i64[i + 2] == height)) {
+                    durations->push_back(entry.data.i64[i + 3]);
+                    break;
+                }
+            }
+        }
+    }
+}
diff --git a/camera/provider/aidl/vts/camera_aidl_test.h b/camera/provider/aidl/vts/camera_aidl_test.h
index f13d6b2..4b4c039 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.h
+++ b/camera/provider/aidl/vts/camera_aidl_test.h
@@ -145,7 +145,9 @@
         ACES,
         ACESCG,
         CIE_XYZ,
-        CIE_LAB
+        CIE_LAB,
+        BT2020_HLG,
+        BT2020_PQ
     };
 
     struct AvailableZSLInputOutput {
@@ -418,6 +420,13 @@
     bool supportsCroppedRawUseCase(const camera_metadata_t *staticMeta);
     bool isPerFrameControl(const camera_metadata_t* staticMeta);
 
+    void getSupportedSizes(const camera_metadata_t* ch, uint32_t tag, int32_t format,
+                           std::vector<std::tuple<size_t, size_t>>* sizes /*out*/);
+
+    void getSupportedDurations(const camera_metadata_t* ch, uint32_t tag, int32_t format,
+                               const std::vector<std::tuple<size_t, size_t>>& sizes,
+                               std::vector<int64_t>* durations /*out*/);
+
   protected:
     // In-flight queue for tracking completion of capture requests.
     struct InFlightRequest {
diff --git a/common/fmq/aidl/Android.bp b/common/fmq/aidl/Android.bp
index 5772b7f..3b022fc 100644
--- a/common/fmq/aidl/Android.bp
+++ b/common/fmq/aidl/Android.bp
@@ -48,5 +48,10 @@
         },
     },
     frozen: true,
-    versions: ["1"],
+    versions_with_info: [
+        {
+            version: "1",
+            imports: ["android.hardware.common-V2"],
+        },
+    ],
 }
diff --git a/compatibility_matrices/compatibility_matrix.4.xml b/compatibility_matrices/compatibility_matrix.4.xml
index c898aab..b9fb3f4 100644
--- a/compatibility_matrices/compatibility_matrix.4.xml
+++ b/compatibility_matrices/compatibility_matrix.4.xml
@@ -7,7 +7,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.audio</name>
         <version>5.0</version>
         <interface>
@@ -15,7 +15,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.audio.effect</name>
         <version>5.0</version>
         <interface>
@@ -199,7 +199,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.graphics.composer</name>
         <version>2.1-3</version>
         <interface>
@@ -207,7 +207,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.graphics.mapper</name>
         <version>2.1</version>
         <version>3.0</version>
diff --git a/compatibility_matrices/compatibility_matrix.5.xml b/compatibility_matrices/compatibility_matrix.5.xml
index c5a1dc2..b374c8c 100644
--- a/compatibility_matrices/compatibility_matrix.5.xml
+++ b/compatibility_matrices/compatibility_matrix.5.xml
@@ -7,7 +7,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.audio</name>
         <version>6.0</version>
         <interface>
@@ -15,7 +15,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.audio.effect</name>
         <version>6.0</version>
         <interface>
@@ -222,7 +222,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.graphics.composer</name>
         <version>2.1-4</version>
         <interface>
@@ -230,7 +230,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.graphics.mapper</name>
         <!-- New, non-Go devices should use 4.0, tested in vts_treble_vintf_vendor_test -->
         <version>2.1</version>
@@ -367,7 +367,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="aidl" optional="false">
+    <hal format="aidl" optional="true">
         <name>android.hardware.power</name>
         <interface>
             <name>IPower</name>
diff --git a/compatibility_matrices/compatibility_matrix.6.xml b/compatibility_matrices/compatibility_matrix.6.xml
index 1c76ee6..40ae655 100644
--- a/compatibility_matrices/compatibility_matrix.6.xml
+++ b/compatibility_matrices/compatibility_matrix.6.xml
@@ -7,7 +7,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.audio</name>
         <version>6.0</version>
         <version>7.0</version>
@@ -16,7 +16,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.audio.effect</name>
         <version>6.0</version>
         <version>7.0</version>
@@ -252,7 +252,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.graphics.composer</name>
         <version>2.1-4</version>
         <interface>
@@ -260,7 +260,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.graphics.mapper</name>
         <!-- New, non-Go devices should use 4.0, tested in vts_treble_vintf_vendor_test -->
         <version>2.1</version>
@@ -423,7 +423,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="aidl" optional="false">
+    <hal format="aidl" optional="true">
         <name>android.hardware.power</name>
         <version>1-2</version>
         <interface>
diff --git a/compatibility_matrices/compatibility_matrix.7.xml b/compatibility_matrices/compatibility_matrix.7.xml
index 67dd717..e5ef954 100644
--- a/compatibility_matrices/compatibility_matrix.7.xml
+++ b/compatibility_matrices/compatibility_matrix.7.xml
@@ -327,7 +327,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="aidl" optional="false">
+    <hal format="aidl" optional="true">
         <name>android.hardware.health</name>
         <version>1</version>
         <interface>
@@ -493,7 +493,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="aidl" optional="false">
+    <hal format="aidl" optional="true">
         <name>android.hardware.power</name>
         <version>2-3</version>
         <interface>
diff --git a/compatibility_matrices/compatibility_matrix.8.xml b/compatibility_matrices/compatibility_matrix.8.xml
index c58f559..310eaa1 100644
--- a/compatibility_matrices/compatibility_matrix.8.xml
+++ b/compatibility_matrices/compatibility_matrix.8.xml
@@ -1,5 +1,5 @@
 <compatibility-matrix version="1.0" type="framework" level="8">
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.audio</name>
         <version>6.0</version>
         <version>7.0-1</version>
@@ -62,7 +62,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.automotive.audiocontrol</name>
-        <version>2</version>
+        <version>2-3</version>
         <interface>
             <name>IAudioControl</name>
             <instance>default</instance>
@@ -84,15 +84,6 @@
             <regex-instance>[a-z]+/[0-9]+</regex-instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.automotive.evs</name>
-        <version>1.0-1</version>
-        <interface>
-            <name>IEvsEnumerator</name>
-            <instance>default</instance>
-            <regex-instance>[a-z]+/[0-9]+</regex-instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.automotive.occupant_awareness</name>
         <version>1</version>
@@ -103,6 +94,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.automotive.vehicle</name>
+        <version>1-2</version>
         <interface>
             <name>IVehicle</name>
             <instance>default</instance>
@@ -282,7 +274,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="aidl" optional="false">
+    <hal format="aidl" optional="true">
         <name>android.hardware.health</name>
         <version>1-2</version>
         <interface>
@@ -395,7 +387,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="aidl" optional="false">
+    <hal format="aidl" optional="true">
         <name>android.hardware.power</name>
         <version>4</version>
         <interface>
diff --git a/current.txt b/current.txt
index a6c4d80..754093f 100644
--- a/current.txt
+++ b/current.txt
@@ -937,5 +937,6 @@
 889b59e3e7a59afa67bf19882a44f51a2f9e43b6556ec52baa9ec3efd1ef7fbe android.hardware.camera.device@3.2::types
 db37a1c757e2e69b1ec9c75a981a6987bd87a131d92ab6acc00e04d19f374281 android.hardware.automotive.vehicle@2.0::types
 997017f581406fca1675d2f612f7ccd73f0d04eadd54bf6212e6cf5971d0872d android.hardware.automotive.vehicle@2.0::types
+06983ffe6d75e90a22503a6d9fd14417f983a36bb060a80ad5915240d69b8a40 android.hardware.automotive.vehicle@2.0::types
 
 # There will be no more HIDL HALs. Use AIDL instead.
diff --git a/drm/aidl/Android.bp b/drm/aidl/Android.bp
index 6273a38..fb04d84 100644
--- a/drm/aidl/Android.bp
+++ b/drm/aidl/Android.bp
@@ -23,7 +23,7 @@
             sdk_version: "module_current",
         },
         ndk: {
-            min_sdk_version: "current",
+            min_sdk_version: "UpsideDownCake",
         },
     },
     double_loadable: true,
diff --git a/drm/aidl/OWNERS b/drm/aidl/OWNERS
index fa8fd20..fe69725 100644
--- a/drm/aidl/OWNERS
+++ b/drm/aidl/OWNERS
@@ -1,4 +1,3 @@
 # Bug component: 49079
-edwinwong@google.com
 kelzhan@google.com
 robertshih@google.com
diff --git a/drm/aidl/vts/OWNERS b/drm/aidl/vts/OWNERS
index e44b93e..fe69725 100644
--- a/drm/aidl/vts/OWNERS
+++ b/drm/aidl/vts/OWNERS
@@ -1,4 +1,3 @@
-edwinwong@google.com
-jtinker@google.com
+# Bug component: 49079
 kelzhan@google.com
 robertshih@google.com
diff --git a/gnss/aidl/vts/gnss_hal_test_cases.cpp b/gnss/aidl/vts/gnss_hal_test_cases.cpp
index 17d65f5..aa8bdfd 100644
--- a/gnss/aidl/vts/gnss_hal_test_cases.cpp
+++ b/gnss/aidl/vts/gnss_hal_test_cases.cpp
@@ -1132,8 +1132,10 @@
     status = iAGnssRil->setRefLocation(agnssReflocation);
     ASSERT_TRUE(status.isOk());
 
-    status = iAGnssRil->injectNiSuplMessageData(std::vector<uint8_t>(), 0);
-    ASSERT_FALSE(status.isOk());
+    if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
+        status = iAGnssRil->injectNiSuplMessageData(std::vector<uint8_t>(), 0);
+        ASSERT_FALSE(status.isOk());
+    }
 }
 
 /*
diff --git a/graphics/common/aidl/android/hardware/graphics/common/Dataspace.aidl b/graphics/common/aidl/android/hardware/graphics/common/Dataspace.aidl
index b44e613..4f2a087 100644
--- a/graphics/common/aidl/android/hardware/graphics/common/Dataspace.aidl
+++ b/graphics/common/aidl/android/hardware/graphics/common/Dataspace.aidl
@@ -87,7 +87,7 @@
      * Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation
      * for RGB conversion.
      */
-    STANDARD_BT709 = 1 << 16,  // 1 << STANDARD_SHIFT
+    STANDARD_BT709 = 1 << 16, // 1 << STANDARD_SHIFT
 
     /**
      * Primaries:       x       y
@@ -377,11 +377,19 @@
     RANGE_LIMITED = 2 << 27, // 2 << RANGE_SHIFT = 0x10000000
 
     /**
-     * Extended range is used for scRGB. Intended for use with
-     * floating point pixel formats. [0.0 - 1.0] is the standard
-     * sRGB space. Values outside the range 0.0 - 1.0 can encode
-     * color outside the sRGB gamut.
-     * Used to blend / merge multiple dataspaces on a single display.
+     * Extended range can be used in combination with FP16 to communicate scRGB or with
+     * SurfaceControl's setExtendedRangeBrightness(SurfaceControl, float, float)
+     * to indicate an HDR range.
+     *
+     * When used with floating point pixel formats and #STANDARD_BT709 then [0.0 - 1.0] is the
+     * standard sRGB space and values outside the range [0.0 - 1.0] can encode
+     * color outside the sRGB gamut. [-0.5, 7.5] is the standard scRGB range.
+     * Used to blend/merge multiple dataspaces on a single display.
+     *
+     * As of Android U in combination with composer3's mixed SDR/HDR feature then this may
+     * be combined with SurfaceControl's setExtendedRangeBrightness(SurfaceControl, float, float)
+     * and other formats such as RGBA_8888 or RGBA_1010102 to communicate a variable HDR
+     * brightness range, which in turn will influence that layer's dimming ratio when composited
      */
     RANGE_EXTENDED = 3 << 27, // 3 << RANGE_SHIFT = 0x18000000
 
@@ -397,7 +405,6 @@
      */
     SRGB_LINEAR = 1 << 16 | 1 << 22 | 1 << 27, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL
 
-
     /**
      * scRGB linear encoding:
      *
@@ -412,7 +419,6 @@
      */
     SCRGB_LINEAR = 1 << 16 | 1 << 22 | 3 << 27, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_EXTENDED
 
-
     /**
      * sRGB gamma encoding:
      *
@@ -428,7 +434,6 @@
      */
     SRGB = 1 << 16 | 2 << 22 | 1 << 27, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_FULL
 
-
     /**
      * scRGB:
      *
@@ -470,8 +475,8 @@
      *
      * Use limited range, SMPTE 170M transfer and BT.601_625 standard.
      */
-    BT601_625 = 2 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
-
+    BT601_625 =
+            2 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
 
     /**
      * ITU-R Recommendation 601 (BT.601) - 525-line
@@ -480,7 +485,8 @@
      *
      * Use limited range, SMPTE 170M transfer and BT.601_525 standard.
      */
-    BT601_525 = 4 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+    BT601_525 =
+            4 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED
 
     /**
      * ITU-R Recommendation 709 (BT.709)
@@ -491,7 +497,6 @@
      */
     BT709 = 1 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED
 
-
     /**
      * SMPTE EG 432-1 and SMPTE RP 431-2.
      *
@@ -501,7 +506,6 @@
      */
     DCI_P3_LINEAR = 10 << 16 | 1 << 22 | 1 << 27, // STANDARD_DCI_P3 | TRANSFER_LINEAR | RANGE_FULL
 
-
     /**
      * SMPTE EG 432-1 and SMPTE RP 431-2.
      *
@@ -513,15 +517,14 @@
      */
     DCI_P3 = 10 << 16 | 5 << 22 | 1 << 27, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL
 
-
     /**
      * Display P3
      *
      * Display P3 uses same primaries and white-point as DCI-P3
      * linear transfer function makes this the same as DCI_P3_LINEAR.
      */
-    DISPLAY_P3_LINEAR = 10 << 16 | 1 << 22 | 1 << 27, // STANDARD_DCI_P3 | TRANSFER_LINEAR | RANGE_FULL
-
+    DISPLAY_P3_LINEAR =
+            10 << 16 | 1 << 22 | 1 << 27, // STANDARD_DCI_P3 | TRANSFER_LINEAR | RANGE_FULL
 
     /**
      * Display P3
@@ -531,7 +534,6 @@
      */
     DISPLAY_P3 = 10 << 16 | 2 << 22 | 1 << 27, // STANDARD_DCI_P3 | TRANSFER_SRGB | RANGE_FULL
 
-
     /**
      * Adobe RGB
      *
@@ -541,7 +543,6 @@
      */
     ADOBE_RGB = 11 << 16 | 4 << 22 | 1 << 27, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL
 
-
     /**
      * ITU-R Recommendation 2020 (BT.2020)
      *
@@ -551,7 +552,6 @@
      */
     BT2020_LINEAR = 6 << 16 | 1 << 22 | 1 << 27, // STANDARD_BT2020 | TRANSFER_LINEAR | RANGE_FULL
 
-
     /**
      * ITU-R Recommendation 2020 (BT.2020)
      *
@@ -570,7 +570,6 @@
      */
     BT2020_PQ = 6 << 16 | 7 << 22 | 1 << 27, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
 
-
     /**
      * Data spaces for non-color formats
      */
@@ -611,7 +610,8 @@
      *
      * Use limited range, SMPTE 170M transfer and BT2020 standard
      */
-    BT2020_ITU = 6 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+    BT2020_ITU =
+            6 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_LIMITED
 
     /**
      * ITU-R Recommendation 2100 (BT.2100)
@@ -621,7 +621,8 @@
      * Use limited/full range, PQ/HLG transfer, and BT2020 standard
      * limited range is the preferred / normative definition for BT.2100
      */
-    BT2020_ITU_PQ = 6 << 16 | 7 << 22 | 2 << 27, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
+    BT2020_ITU_PQ =
+            6 << 16 | 7 << 22 | 2 << 27, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
     BT2020_ITU_HLG = 6 << 16 | 8 << 22 | 2 << 27, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED
     BT2020_HLG = 6 << 16 | 8 << 22 | 1 << 27, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL
 
@@ -687,5 +688,6 @@
      *
      * Use full range, SMPTE 170M transfer and BT.709 standard.
      */
-    BT709_FULL_RANGE = 1 << 16 | 3 << 22 | 1 << 27, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_FULL
+    BT709_FULL_RANGE =
+            1 << 16 | 3 << 22 | 1 << 27, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_FULL
 }
diff --git a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
index 4822678..9444d89 100644
--- a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
+++ b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
@@ -666,11 +666,13 @@
         ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::TearDown());
     }
 
-    NativeHandleWrapper allocate() {
+    NativeHandleWrapper allocate() { return allocate(mDisplayWidth, mDisplayHeight); }
+
+    NativeHandleWrapper allocate(uint32_t width, uint32_t height) {
         uint64_t usage =
                 static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN |
                                       BufferUsage::COMPOSER_OVERLAY);
-        return mGralloc->allocate(mDisplayWidth, mDisplayHeight, 1, PixelFormat::RGBA_8888, usage);
+        return mGralloc->allocate(width, height, 1, PixelFormat::RGBA_8888, usage);
     }
 
     void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); }
@@ -884,6 +886,106 @@
 }
 
 /**
+ * Test IComposerClient::Command::SET_LAYER_BUFFER with the behavior used for clearing buffer slots.
+ */
+TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_BUFFER_multipleTimes) {
+    // A placeholder buffer used to clear buffer slots
+    auto clearSlotBuffer = allocate(1u, 1u);
+
+    //
+    // Set the layer buffer to the first buffer
+    //
+    auto handle1 = allocate();
+    ASSERT_NE(nullptr, handle1.get());
+    IComposerClient::Rect displayFrame{0, 0, mDisplayWidth, mDisplayHeight};
+    Layer layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));
+    mWriter->selectDisplay(mPrimaryDisplay);
+    mWriter->selectLayer(layer);
+    mWriter->setLayerCompositionType(IComposerClient::Composition::DEVICE);
+    mWriter->setLayerDisplayFrame(displayFrame);
+    mWriter->setLayerBuffer(0, handle1.get(), -1);
+    mWriter->setLayerDataspace(Dataspace::UNKNOWN);
+    mWriter->validateDisplay();
+    execute();
+    if (mReader->mCompositionChanges.size() != 0) {
+        GTEST_SUCCEED() << "Composition change requested, skipping test";
+        return;
+    }
+    ASSERT_EQ(0, mReader->mErrors.size());
+    mWriter->selectDisplay(mPrimaryDisplay);
+    mWriter->presentDisplay();
+    execute();
+    ASSERT_EQ(0, mReader->mErrors.size());
+
+    //
+    // Set the layer buffer to the second buffer
+    //
+    auto handle2 = allocate();
+    ASSERT_NE(nullptr, handle2.get());
+    mWriter->selectDisplay(mPrimaryDisplay);
+    mWriter->selectLayer(layer);
+    mWriter->setLayerCompositionType(IComposerClient::Composition::DEVICE);
+    mWriter->setLayerDisplayFrame(displayFrame);
+    mWriter->setLayerBuffer(1, handle2.get(), -1);
+    mWriter->setLayerDataspace(Dataspace::UNKNOWN);
+    mWriter->validateDisplay();
+    execute();
+    if (mReader->mCompositionChanges.size() != 0) {
+        GTEST_SUCCEED() << "Composition change requested, skipping test";
+        return;
+    }
+    ASSERT_EQ(0, mReader->mErrors.size());
+    mWriter->selectDisplay(mPrimaryDisplay);
+    mWriter->presentDisplay();
+    execute();
+    ASSERT_EQ(0, mReader->mErrors.size());
+
+    //
+    // Set the layer buffer to the third buffer
+    //
+    auto handle3 = allocate();
+    ASSERT_NE(nullptr, handle3.get());
+    mWriter->selectDisplay(mPrimaryDisplay);
+    mWriter->selectLayer(layer);
+    mWriter->setLayerCompositionType(IComposerClient::Composition::DEVICE);
+    mWriter->setLayerDisplayFrame(displayFrame);
+    mWriter->setLayerBuffer(2, handle3.get(), -1);
+    mWriter->setLayerDataspace(Dataspace::UNKNOWN);
+    mWriter->validateDisplay();
+    execute();
+    if (mReader->mCompositionChanges.size() != 0) {
+        GTEST_SUCCEED() << "Composition change requested, skipping test";
+        return;
+    }
+    ASSERT_EQ(0, mReader->mErrors.size());
+    mWriter->selectDisplay(mPrimaryDisplay);
+    mWriter->presentDisplay();
+    execute();
+    ASSERT_EQ(0, mReader->mErrors.size());
+
+    // Ensure we can clear multiple buffer slots and then restore the active buffer at the end
+    mWriter->selectDisplay(mPrimaryDisplay);
+    mWriter->selectLayer(layer);
+    mWriter->setLayerBuffer(0, clearSlotBuffer.get(), -1);
+    mWriter->selectDisplay(mPrimaryDisplay);
+    mWriter->selectLayer(layer);
+    mWriter->setLayerBuffer(1, clearSlotBuffer.get(), -1);
+    mWriter->selectDisplay(mPrimaryDisplay);
+    mWriter->selectLayer(layer);
+    mWriter->setLayerBuffer(2, nullptr, -1);
+    mWriter->validateDisplay();
+    execute();
+    ASSERT_EQ(0, mReader->mErrors.size());
+
+    mWriter->selectDisplay(mPrimaryDisplay);
+    mWriter->presentDisplay();
+    execute();
+    ASSERT_EQ(0, mReader->mErrors.size());
+}
+
+/**
  * Test IComposerClient::Command::SET_LAYER_SURFACE_DAMAGE.
  */
 TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_SURFACE_DAMAGE) {
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCapability.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCapability.aidl
index 0e2d72b..6eba887 100644
--- a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCapability.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCapability.aidl
@@ -42,5 +42,4 @@
   AUTO_LOW_LATENCY_MODE = 5,
   SUSPEND = 6,
   DISPLAY_IDLE_TIMER = 7,
-  MULTI_THREADED_PRESENT = 8,
 }
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCapability.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCapability.aidl
index 7154d74..f4b2984 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCapability.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCapability.aidl
@@ -80,20 +80,4 @@
      * IComposerCallback.onVsyncIdle.
      */
     DISPLAY_IDLE_TIMER = 7,
-    /**
-     * Indicates that both the composer HAL implementation and the given display
-     * support calling executeCommands concurrently from separate threads.
-     * executeCommands for a particular display will never run concurrently to
-     * any other executeCommands for the same display. In addition, the
-     * CommandResultPayload must only reference displays included in the
-     * DisplayCommands passed to executeCommands. Displays referenced from
-     * separate threads must have minimal interference with one another. If a
-     * HWC-managed display has this capability, SurfaceFlinger can run
-     * executeCommands for this display concurrently with other displays with the
-     * same capability.
-     * @see IComposerClient.executeCommands
-     * @see DisplayCommand.presentDisplay
-     * @see DisplayCommand.validateDisplay
-     */
-    MULTI_THREADED_PRESENT = 8,
 }
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
index 746330b..3ea5bf5 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
@@ -118,6 +118,12 @@
                 [&](const Capability& activeCapability) { return activeCapability == capability; });
     }
 
+    int getInterfaceVersion() {
+        const auto& [versionStatus, version] = mComposerClient->getInterfaceVersion();
+        EXPECT_TRUE(versionStatus.isOk());
+        return version;
+    }
+
     const VtsDisplay& getPrimaryDisplay() const { return mDisplays[0]; }
 
     int64_t getPrimaryDisplayId() const { return getPrimaryDisplay().getDisplayId(); }
@@ -874,9 +880,7 @@
 }
 
 TEST_P(GraphicsComposerAidlTest, GetOverlaySupport) {
-    const auto& [versionStatus, version] = mComposerClient->getInterfaceVersion();
-    ASSERT_TRUE(versionStatus.isOk());
-    if (version == 1) {
+    if (getInterfaceVersion() <= 1) {
         GTEST_SUCCEED() << "Device does not support the new API for overlay support";
         return;
     }
@@ -1225,17 +1229,21 @@
         });
     }
 
-    sp<GraphicBuffer> allocate(::android::PixelFormat pixelFormat) {
+    sp<GraphicBuffer> allocate(uint32_t width, uint32_t height,
+                               ::android::PixelFormat pixelFormat) {
         return sp<GraphicBuffer>::make(
-                static_cast<uint32_t>(getPrimaryDisplay().getDisplayWidth()),
-                static_cast<uint32_t>(getPrimaryDisplay().getDisplayHeight()), pixelFormat,
-                /*layerCount*/ 1U,
-                (static_cast<uint64_t>(common::BufferUsage::CPU_WRITE_OFTEN) |
-                 static_cast<uint64_t>(common::BufferUsage::CPU_READ_OFTEN) |
-                 static_cast<uint64_t>(common::BufferUsage::COMPOSER_OVERLAY)),
+                width, height, pixelFormat, /*layerCount*/ 1U,
+                static_cast<uint64_t>(common::BufferUsage::CPU_WRITE_OFTEN) |
+                        static_cast<uint64_t>(common::BufferUsage::CPU_READ_OFTEN) |
+                        static_cast<uint64_t>(common::BufferUsage::COMPOSER_OVERLAY),
                 "VtsHalGraphicsComposer3_TargetTest");
     }
 
+    sp<GraphicBuffer> allocate(::android::PixelFormat pixelFormat) {
+        return allocate(static_cast<uint32_t>(getPrimaryDisplay().getDisplayWidth()),
+                        static_cast<uint32_t>(getPrimaryDisplay().getDisplayHeight()), pixelFormat);
+    }
+
     void sendRefreshFrame(const VtsDisplay& display, const VsyncPeriodChangeTimeline* timeline) {
         if (timeline != nullptr) {
             // Refresh time should be before newVsyncAppliedTimeNanos
@@ -1749,6 +1757,104 @@
     execute();
 }
 
+TEST_P(GraphicsComposerAidlCommandTest, SetLayerBufferSlotsToClear) {
+    // Older HAL versions use a backwards compatible way of clearing buffer slots
+    const auto& [versionStatus, version] = mComposerClient->getInterfaceVersion();
+    ASSERT_TRUE(versionStatus.isOk());
+    if (version <= 1) {
+        GTEST_SUCCEED() << "HAL at version 1 or lower does not have "
+                           "LayerCommand::bufferSlotsToClear.";
+        return;
+    }
+
+    const auto& [layerStatus, layer] =
+            mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
+    EXPECT_TRUE(layerStatus.isOk());
+    auto& writer = getWriter(getPrimaryDisplayId());
+
+    // setup 3 buffers in the buffer cache, with the last buffer being active
+    // then emulate the Android platform code that clears all 3 buffer slots
+
+    const auto buffer1 = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+    ASSERT_NE(nullptr, buffer1);
+    const auto handle1 = buffer1->handle;
+    writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, handle1, /*acquireFence*/ -1);
+    execute();
+    ASSERT_TRUE(mReader.takeErrors().empty());
+
+    const auto buffer2 = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+    ASSERT_NE(nullptr, buffer2);
+    const auto handle2 = buffer2->handle;
+    writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 1, handle2, /*acquireFence*/ -1);
+    execute();
+    ASSERT_TRUE(mReader.takeErrors().empty());
+
+    const auto buffer3 = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+    ASSERT_NE(nullptr, buffer3);
+    const auto handle3 = buffer3->handle;
+    writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 2, handle3, /*acquireFence*/ -1);
+    execute();
+    ASSERT_TRUE(mReader.takeErrors().empty());
+
+    // Ensure we can clear all 3 buffer slots, even the active buffer - it is assumed the
+    // current active buffer's slot will be cleared, but still remain the active buffer and no
+    // errors will occur.
+    writer.setLayerBufferSlotsToClear(getPrimaryDisplayId(), layer, {0, 1, 2});
+    execute();
+    ASSERT_TRUE(mReader.takeErrors().empty());
+}
+
+TEST_P(GraphicsComposerAidlCommandTest, SetLayerBufferMultipleTimes) {
+    const auto& [layerStatus, layer] =
+            mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
+    EXPECT_TRUE(layerStatus.isOk());
+    auto& writer = getWriter(getPrimaryDisplayId());
+
+    // Setup 3 buffers in the buffer cache, with the last buffer being active. Then, emulate the
+    // Android platform code that clears all 3 buffer slots by setting all but the active buffer
+    // slot to a placeholder buffer, and then restoring the active buffer.
+
+    // This is used on HALs that don't support setLayerBufferSlotsToClear (version <= 3.1).
+
+    const auto buffer1 = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+    ASSERT_NE(nullptr, buffer1);
+    const auto handle1 = buffer1->handle;
+    writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, handle1, /*acquireFence*/ -1);
+    execute();
+    ASSERT_TRUE(mReader.takeErrors().empty());
+
+    const auto buffer2 = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+    ASSERT_NE(nullptr, buffer2);
+    const auto handle2 = buffer2->handle;
+    writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 1, handle2, /*acquireFence*/ -1);
+    execute();
+    ASSERT_TRUE(mReader.takeErrors().empty());
+
+    const auto buffer3 = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+    ASSERT_NE(nullptr, buffer3);
+    const auto handle3 = buffer3->handle;
+    writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 2, handle3, /*acquireFence*/ -1);
+    execute();
+    ASSERT_TRUE(mReader.takeErrors().empty());
+
+    // Older versions of the HAL clear all but the active buffer slot with a placeholder buffer,
+    // and then restoring the current active buffer at the end
+    auto clearSlotBuffer = allocate(1u, 1u, ::android::PIXEL_FORMAT_RGB_888);
+    ASSERT_NE(nullptr, clearSlotBuffer);
+    auto clearSlotBufferHandle = clearSlotBuffer->handle;
+
+    // clear buffer slots 0 and 1 with new layer commands... and then...
+    writer.setLayerBufferWithNewCommand(getPrimaryDisplayId(), layer, /* slot */ 0,
+                                        clearSlotBufferHandle, /*acquireFence*/ -1);
+    writer.setLayerBufferWithNewCommand(getPrimaryDisplayId(), layer, /* slot */ 1,
+                                        clearSlotBufferHandle, /*acquireFence*/ -1);
+    // ...reset the layer buffer to the current active buffer slot with a final new command
+    writer.setLayerBufferWithNewCommand(getPrimaryDisplayId(), layer, /*slot*/ 2, nullptr,
+                                        /*acquireFence*/ -1);
+    execute();
+    ASSERT_TRUE(mReader.takeErrors().empty());
+}
+
 TEST_P(GraphicsComposerAidlCommandTest, SetLayerSurfaceDamage) {
     const auto& [layerStatus, layer] =
             mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
@@ -2290,6 +2396,11 @@
 }
 
 TEST_P(GraphicsComposerAidlCommandTest, SetRefreshRateChangedCallbackDebug_Unsupported) {
+    if (getInterfaceVersion() <= 1) {
+        GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is "
+                           "not supported on older version of the service";
+        return;
+    }
     if (!hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG)) {
         auto status = mComposerClient->setRefreshRateChangedCallbackDebugEnabled(
                 getPrimaryDisplayId(), /*enabled*/ true);
@@ -2306,6 +2417,11 @@
 }
 
 TEST_P(GraphicsComposerAidlCommandTest, SetRefreshRateChangedCallbackDebug_Enabled) {
+    if (getInterfaceVersion() <= 1) {
+        GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is "
+                           "not supported on older version of the service";
+        return;
+    }
     if (!hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG)) {
         GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is not supported";
         return;
@@ -2335,6 +2451,11 @@
 
 TEST_P(GraphicsComposerAidlCommandTest,
        SetRefreshRateChangedCallbackDebugEnabled_noCallbackWhenIdle) {
+    if (getInterfaceVersion() <= 1) {
+        GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is "
+                           "not supported on older version of the service";
+        return;
+    }
     if (!hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG)) {
         GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is not supported";
         return;
@@ -2392,6 +2513,11 @@
 
 TEST_P(GraphicsComposerAidlCommandTest,
        SetRefreshRateChangedCallbackDebugEnabled_SetActiveConfigWithConstraints) {
+    if (getInterfaceVersion() <= 1) {
+        GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is "
+                           "not supported on older version of the service";
+        return;
+    }
     if (!hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG)) {
         GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is not supported";
         return;
@@ -2494,29 +2620,13 @@
     }
 }
 
-TEST_P(GraphicsComposerAidlCommandTest, MultiThreadedPresent) {
-    std::vector<VtsDisplay*> displays;
-    for (auto& display : mDisplays) {
-        if (hasDisplayCapability(display.getDisplayId(),
-                                 DisplayCapability::MULTI_THREADED_PRESENT)) {
-            displays.push_back(&display);
-        }
-    }
-    if (displays.size() <= 1u) {
-        return;
-    }
-    // TODO(b/251842321): Try to present on multiple threads.
-}
-
 /**
  * Test Capability::SKIP_VALIDATE
  *
  * Capability::SKIP_VALIDATE has been deprecated and should not be enabled.
  */
 TEST_P(GraphicsComposerAidlCommandTest, SkipValidateDeprecatedTest) {
-    const auto& [versionStatus, version] = mComposerClient->getInterfaceVersion();
-    ASSERT_TRUE(versionStatus.isOk());
-    if (version <= 1) {
+    if (getInterfaceVersion() <= 1) {
         GTEST_SUCCEED() << "HAL at version 1 or lower can contain Capability::SKIP_VALIDATE.";
         return;
     }
@@ -2583,4 +2693,4 @@
     }
 
     return RUN_ALL_TESTS();
-}
\ No newline at end of file
+}
diff --git a/health/aidl/vts/functional/VtsHalHealthTargetTest.cpp b/health/aidl/vts/functional/VtsHalHealthTargetTest.cpp
index 69d4789..783ce11 100644
--- a/health/aidl/vts/functional/VtsHalHealthTargetTest.cpp
+++ b/health/aidl/vts/functional/VtsHalHealthTargetTest.cpp
@@ -257,7 +257,7 @@
     BatteryChargingPolicy value;
 
     /* set ChargingPolicy*/
-    status = health->setChargingPolicy(static_cast<BatteryChargingPolicy>(2));  // LONG_LIFE
+    status = health->setChargingPolicy(BatteryChargingPolicy::LONG_LIFE);
     ASSERT_THAT(status, AnyOf(IsOk(), ExceptionIs(EX_UNSUPPORTED_OPERATION)));
     if (!status.isOk()) return;
 
@@ -265,7 +265,9 @@
     status = health->getChargingPolicy(&value);
     ASSERT_THAT(status, AnyOf(IsOk(), ExceptionIs(EX_UNSUPPORTED_OPERATION)));
     if (!status.isOk()) return;
-    ASSERT_THAT(static_cast<int>(value), Eq(2));
+    // the result of getChargingPolicy will be one of default(1), ADAPTIVE_AON(2)
+    // ADAPTIVE_AC(3) or LONG_LIFE(4). default(1) means NOT_SUPPORT
+    ASSERT_THAT(static_cast<int>(value), AnyOf(Eq(1), Eq(4)));
 }
 
 MATCHER(IsValidHealthData, "") {
diff --git a/identity/OWNERS b/identity/OWNERS
index 6969910..9353bbc 100644
--- a/identity/OWNERS
+++ b/identity/OWNERS
@@ -1,2 +1,4 @@
+# Bug component: 1084909
+
 swillden@google.com
 zeuthen@google.com
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index 5e303bb..6f7ab54 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -61,16 +61,3 @@
     ],
     require_root: true,
 }
-
-java_test_host {
-    name: "IdentityCredentialImplementedTest",
-    libs: [
-        "tradefed",
-        "vts-core-tradefed-harness",
-    ],
-    srcs: ["src/**/*.java"],
-    test_suites: [
-        "vts",
-    ],
-    test_config: "IdentityCredentialImplementedTest.xml",
-}
diff --git a/identity/aidl/vts/IdentityCredentialImplementedTest.xml b/identity/aidl/vts/IdentityCredentialImplementedTest.xml
deleted file mode 100644
index 4276ae6..0000000
--- a/identity/aidl/vts/IdentityCredentialImplementedTest.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-<configuration description="Runs IdentityCredentialImplementedTest">
-  <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
-
-  <test class="com.android.tradefed.testtype.HostTest" >
-    <option name="jar" value="IdentityCredentialImplementedTest.jar" />
-  </test>
-</configuration>
diff --git a/identity/aidl/vts/src/com/android/tests/security/identity/IdentityCredentialImplementedTest.java b/identity/aidl/vts/src/com/android/tests/security/identity/IdentityCredentialImplementedTest.java
deleted file mode 100644
index 4936f8c..0000000
--- a/identity/aidl/vts/src/com/android/tests/security/identity/IdentityCredentialImplementedTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tests.security.identity;
-
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-
-import android.platform.test.annotations.RequiresDevice;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-// This is a host-test which executes shell commands on the device. It would be
-// nicer to have this be a Device test (like CTS) but this is currently not
-// possible, see https://source.android.com/docs/core/tests/vts
-
-@RunWith(DeviceJUnit4ClassRunner.class)
-public class IdentityCredentialImplementedTest extends BaseHostJUnit4Test {
-    // Returns the ro.vendor.api_level or 0 if not set.
-    //
-    // Throws NumberFormatException if ill-formatted.
-    //
-    // Throws DeviceNotAvailableException if device is not available.
-    //
-    private int getVendorApiLevel() throws NumberFormatException, DeviceNotAvailableException {
-        String vendorApiLevelString =
-                getDevice().executeShellCommand("getprop ro.vendor.api_level").trim();
-        if (vendorApiLevelString.isEmpty()) {
-            return 0;
-        }
-        return Integer.parseInt(vendorApiLevelString);
-    }
-
-    // As of Android 14 VSR (vendor API level 34), Identity Credential is required at feature
-    // version 202301 or later.
-    @RequiresDevice
-    @Test
-    public void testIdentityCredentialIsImplemented() throws Exception {
-        int vendorApiLevel = getVendorApiLevel();
-        assumeTrue(vendorApiLevel >= 34);
-
-        final String minimumFeatureVersionNeeded = "202301";
-
-        String result = getDevice().executeShellCommand(
-                "pm has-feature android.hardware.identity_credential "
-                + minimumFeatureVersionNeeded);
-        if (!result.trim().equals("true")) {
-            fail("Identity Credential feature version " + minimumFeatureVersionNeeded
-                    + " required but not found");
-        }
-    }
-}
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
index dcf8451..e344458 100644
--- a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
@@ -736,8 +736,8 @@
                     // If a sync fence is returned, try start another run waiting for the sync
                     // fence.
                     if (testConfig.reusable) {
-                        ret = execution->executeFenced(waitFor, kNoDeadline, kNoDuration,
-                                                       &executionResult);
+                        // Nothing to do because at most one execution may occur on a reusable
+                        // execution object at any given time.
                     } else if (testConfig.useConfig) {
                         ret = preparedModel->executeFencedWithConfig(
                                 request, waitFor,
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
index 5539b9c..316c308 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
@@ -565,9 +565,9 @@
     serial = GetRandomSerialNumber();
 
     ::android::hardware::radio::V1_5::RadioAccessSpecifier::Bands band17;
-    band17.eutranBands() = {::android::hardware::radio::V1_5::EutranBands::BAND_17};
+    band17.eutranBands({::android::hardware::radio::V1_5::EutranBands::BAND_17});
     ::android::hardware::radio::V1_5::RadioAccessSpecifier::Bands band20;
-    band20.eutranBands() = {::android::hardware::radio::V1_5::EutranBands::BAND_20};
+    band20.eutranBands({::android::hardware::radio::V1_5::EutranBands::BAND_20});
     ::android::hardware::radio::V1_5::RadioAccessSpecifier specifier17 = {
             .radioAccessNetwork = ::android::hardware::radio::V1_5::RadioAccessNetworks::EUTRAN,
             .bands = band17,
diff --git a/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl b/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl
index 8c0dd6d..800494a 100644
--- a/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl
+++ b/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl
@@ -117,6 +117,9 @@
      * closed by this operation.
      * HAL service must send onStateChange() with connected equal to true
      * after resetting and all the re-initialization has been successfully completed.
+     *
+     * @throws ServiceSpecificException on error with the following code:
+     *  - FAILED if the service was unable to reset the secure element.
      */
     void reset();
 
diff --git a/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp b/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
index 0925a21..97b4e27 100644
--- a/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
+++ b/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
@@ -83,10 +83,15 @@
 
     void expectCallbackHistory(std::vector<bool>&& want) {
         std::unique_lock<std::mutex> l(m);
-        cv.wait_for(l, 2s, [&]() { return history.size() >= want.size(); });
+        cv.wait_for(l, 5s, [&]() { return history.size() >= want.size(); });
         EXPECT_THAT(history, ElementsAreArray(want));
     }
 
+    void resetCallbackHistory() {
+        std::unique_lock<std::mutex> l(m);
+        history.clear();
+    }
+
   private:
     std::mutex m;  // guards history
     std::condition_variable cv;
@@ -118,7 +123,9 @@
     }
 
     void TearDown() override {
+        secure_element_callback_->resetCallbackHistory();
         EXPECT_OK(secure_element_->reset());
+        secure_element_callback_->expectCallbackHistory({false, true});
         secure_element_ = nullptr;
         secure_element_callback_ = nullptr;
     }
@@ -284,14 +291,21 @@
 
 TEST_P(SecureElementAidl, transmit) {
     std::vector<uint8_t> response;
+    LogicalChannelResponse logical_channel_response;
 
-    // transmit called after init shall succeed.
-    // Note: no channel is opened for this test and the transmit
-    // response will have the status SW_LOGICAL_CHANNEL_NOT_SUPPORTED.
-    // The transmit response shall be larger than 2 bytes as it includes the
-    // status code.
-    EXPECT_OK(secure_element_->transmit(kDataApdu, &response));
-    EXPECT_GE(response.size(), 2u);
+    // Note: no channel is opened for this test
+    // transmit() will return an empty response with the error
+    // code CHANNEL_NOT_AVAILABLE when the SE cannot be
+    // communicated with.
+    EXPECT_ERR(secure_element_->transmit(kDataApdu, &response));
+
+    EXPECT_OK(secure_element_->openLogicalChannel(kSelectableAid, 0x00, &logical_channel_response));
+    EXPECT_GE(logical_channel_response.selectResponse.size(), 2u);
+    EXPECT_GE(logical_channel_response.channelNumber, 1u);
+    EXPECT_LE(logical_channel_response.channelNumber, 19u);
+
+    // transmit called on the logical channel should succeed.
+    EXPECT_EQ(transmit(logical_channel_response.channelNumber), 0x9000);
 }
 
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SecureElementAidl);
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index c6b8906..63b2e73 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -26,6 +26,7 @@
 #include <openssl/ec.h>
 #include <openssl/evp.h>
 #include <openssl/mem.h>
+#include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
 #include <cutils/properties.h>
@@ -590,8 +591,7 @@
     return name.substr(pos + 1);
 }
 
-bool matching_rp_instance(const string& km_name,
-                          std::shared_ptr<IRemotelyProvisionedComponent>* rp) {
+std::shared_ptr<IRemotelyProvisionedComponent> matching_rp_instance(const std::string& km_name) {
     string km_suffix = device_suffix(km_name);
 
     vector<string> rp_names =
@@ -601,11 +601,10 @@
         // KeyMint instance, assume they match.
         if (device_suffix(rp_name) == km_suffix && AServiceManager_isDeclared(rp_name.c_str())) {
             ::ndk::SpAIBinder binder(AServiceManager_waitForService(rp_name.c_str()));
-            *rp = IRemotelyProvisionedComponent::fromBinder(binder);
-            return true;
+            return IRemotelyProvisionedComponent::fromBinder(binder);
         }
     }
-    return false;
+    return nullptr;
 }
 
 }  // namespace
@@ -1061,6 +1060,42 @@
 }
 
 /*
+ * NewKeyGenerationTest.RsaWithSpecifiedValidity
+ *
+ * Verifies that KeyMint respects specified NOT_BEFORE and NOT_AFTER certificate dates.
+ */
+TEST_P(NewKeyGenerationTest, RsaWithSpecifiedValidity) {
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+    ASSERT_EQ(ErrorCode::OK,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .RsaSigningKey(2048, 65537)
+                                  .Digest(Digest::NONE)
+                                  .Padding(PaddingMode::NONE)
+                                  .Authorization(TAG_CERTIFICATE_NOT_BEFORE,
+                                                 1183806000000 /* 2007-07-07T11:00:00Z */)
+                                  .Authorization(TAG_CERTIFICATE_NOT_AFTER,
+                                                 1916049600000 /* 2030-09-19T12:00:00Z */),
+                          &key_blob, &key_characteristics));
+    ASSERT_GT(cert_chain_.size(), 0);
+
+    X509_Ptr cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+    ASSERT_TRUE(!!cert.get());
+
+    const ASN1_TIME* not_before = X509_get0_notBefore(cert.get());
+    ASSERT_NE(not_before, nullptr);
+    time_t not_before_time;
+    ASSERT_EQ(ASN1_TIME_to_time_t(not_before, &not_before_time), 1);
+    EXPECT_EQ(not_before_time, 1183806000);
+
+    const ASN1_TIME* not_after = X509_get0_notAfter(cert.get());
+    ASSERT_NE(not_after, nullptr);
+    time_t not_after_time;
+    ASSERT_EQ(ASN1_TIME_to_time_t(not_after, &not_after_time), 1);
+    EXPECT_EQ(not_after_time, 1916049600);
+}
+
+/*
  * NewKeyGenerationTest.RsaWithAttestation
  *
  * Verifies that keymint can generate all required RSA key sizes with attestation, and that the
@@ -1140,11 +1175,14 @@
         GTEST_SKIP() << "RKP support is not required on this platform";
     }
 
-    // There should be an IRemotelyProvisionedComponent instance associated with the KeyMint
-    // instance.
-    std::shared_ptr<IRemotelyProvisionedComponent> rp;
-    ASSERT_TRUE(matching_rp_instance(GetParam(), &rp))
-            << "No IRemotelyProvisionedComponent found that matches KeyMint device " << GetParam();
+    // Check for an IRemotelyProvisionedComponent instance associated with the
+    // KeyMint instance.
+    std::shared_ptr<IRemotelyProvisionedComponent> rp = matching_rp_instance(GetParam());
+    if (rp == nullptr && SecLevel() == SecurityLevel::STRONGBOX) {
+        GTEST_SKIP() << "Encountered StrongBox implementation that does not support RKP";
+    }
+    ASSERT_NE(rp, nullptr) << "No IRemotelyProvisionedComponent found that matches KeyMint device "
+                           << GetParam();
 
     // Generate a P-256 keypair to use as an attestation key.
     MacedPublicKey macedPubKey;
@@ -1218,11 +1256,14 @@
         GTEST_SKIP() << "RKP support is not required on this platform";
     }
 
-    // There should be an IRemotelyProvisionedComponent instance associated with the KeyMint
-    // instance.
-    std::shared_ptr<IRemotelyProvisionedComponent> rp;
-    ASSERT_TRUE(matching_rp_instance(GetParam(), &rp))
-            << "No IRemotelyProvisionedComponent found that matches KeyMint device " << GetParam();
+    // Check for an IRemotelyProvisionedComponent instance associated with the
+    // KeyMint instance.
+    std::shared_ptr<IRemotelyProvisionedComponent> rp = matching_rp_instance(GetParam());
+    if (rp == nullptr && SecLevel() == SecurityLevel::STRONGBOX) {
+        GTEST_SKIP() << "Encountered StrongBox implementation that does not support RKP";
+    }
+    ASSERT_NE(rp, nullptr) << "No IRemotelyProvisionedComponent found that matches KeyMint device "
+                           << GetParam();
 
     // Generate a P-256 keypair to use as an attestation key.
     MacedPublicKey macedPubKey;
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
index 086ee79..ffcaa95 100644
--- a/security/keymint/support/remote_prov_utils.cpp
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -22,6 +22,7 @@
 #include "aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h"
 
 #include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
+#include <android-base/macros.h>
 #include <android-base/properties.h>
 #include <cppbor.h>
 #include <hwtrust/hwtrust.h>
@@ -43,6 +44,7 @@
 constexpr int32_t kBccPayloadSubjPubKey = -4670552;
 constexpr int32_t kBccPayloadKeyUsage = -4670553;
 constexpr int kP256AffinePointSize = 32;
+constexpr uint32_t kNumTeeDeviceInfoEntries = 14;
 
 using EC_KEY_Ptr = bssl::UniquePtr<EC_KEY>;
 using EVP_PKEY_Ptr = bssl::UniquePtr<EVP_PKEY>;
@@ -388,6 +390,11 @@
     return entryName + " has an invalid value.\n";
 }
 
+bool isTeeDeviceInfo(const cppbor::Map& devInfo) {
+    return devInfo.get("security_level") && devInfo.get("security_level")->asTstr() &&
+           devInfo.get("security_level")->asTstr()->value() == "tee";
+}
+
 ErrMsgOr<std::unique_ptr<cppbor::Map>> parseAndValidateDeviceInfo(
         const std::vector<uint8_t>& deviceInfoBytes, IRemotelyProvisionedComponent* provisionable,
         bool isFactory) {
@@ -396,6 +403,21 @@
     const cppbor::Array kValidSecurityLevels = {"tee", "strongbox"};
     const cppbor::Array kValidAttIdStates = {"locked", "open"};
     const cppbor::Array kValidFused = {0, 1};
+    constexpr std::array<std::string_view, kNumTeeDeviceInfoEntries> kDeviceInfoKeys = {
+            "brand",
+            "manufacturer",
+            "product",
+            "model",
+            "device",
+            "vb_state",
+            "bootloader_state",
+            "vbmeta_digest",
+            "os_version",
+            "system_patch_level",
+            "boot_patch_level",
+            "vendor_patch_level",
+            "security_level",
+            "fused"};
 
     struct AttestationIdEntry {
         const char* id;
@@ -439,20 +461,48 @@
     }
 
     std::string error;
+    std::string tmp;
+    std::set<std::string_view> previousKeys;
     switch (info.versionNumber) {
         case 3:
+            if (isTeeDeviceInfo(*parsed) && parsed->size() != kNumTeeDeviceInfoEntries) {
+                error += fmt::format(
+                        "Err: Incorrect number of device info entries. Expected {} but got "
+                        "{}\n",
+                        kNumTeeDeviceInfoEntries, parsed->size());
+            }
+            // TEE IRPC instances require all entries to be present in DeviceInfo. Non-TEE instances
+            // may omit `os_version`
+            if (!isTeeDeviceInfo(*parsed) && (parsed->size() != kNumTeeDeviceInfoEntries &&
+                                              parsed->size() != kNumTeeDeviceInfoEntries - 1)) {
+                error += fmt::format(
+                        "Err: Incorrect number of device info entries. Expected {} or {} but got "
+                        "{}\n",
+                        kNumTeeDeviceInfoEntries - 1, kNumTeeDeviceInfoEntries, parsed->size());
+            }
+            for (auto& [key, _] : *parsed) {
+                const std::string& keyValue = key->asTstr()->value();
+                if (!previousKeys.insert(keyValue).second) {
+                    error += "Err: Duplicate device info entry: <" + keyValue + ">,\n";
+                }
+                if (std::find(kDeviceInfoKeys.begin(), kDeviceInfoKeys.end(), keyValue) ==
+                    kDeviceInfoKeys.end()) {
+                    error += "Err: Unrecognized key entry: <" + key->asTstr()->value() + ">,\n";
+                }
+            }
+            FALLTHROUGH_INTENDED;
         case 2:
             for (const auto& entry : kAttestationIdEntrySet) {
-                error += checkMapEntry(isFactory && !entry.alwaysValidate, *parsed, cppbor::TSTR,
-                                       entry.id);
+                tmp = checkMapEntry(isFactory && !entry.alwaysValidate, *parsed, cppbor::TSTR,
+                                    entry.id);
             }
-            if (!error.empty()) {
-                return error +
-                       "Attestation IDs are missing or malprovisioned. If this test is being\n"
-                       "run against an early proto or EVT build, this error is probably WAI\n"
-                       "and indicates that Device IDs were not provisioned in the factory. If\n"
-                       "this error is returned on a DVT or later build revision, then\n"
-                       "something is likely wrong with the factory provisioning process.";
+            if (!tmp.empty()) {
+                error += tmp +
+                         "Attestation IDs are missing or malprovisioned. If this test is being\n"
+                         "run against an early proto or EVT build, this error is probably WAI\n"
+                         "and indicates that Device IDs were not provisioned in the factory. If\n"
+                         "this error is returned on a DVT or later build revision, then\n"
+                         "something is likely wrong with the factory provisioning process.";
             }
             // TODO: Refactor the KeyMint code that validates these fields and include it here.
             error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "vb_state", kValidVbStates);
@@ -465,8 +515,7 @@
             error += checkMapEntry(isFactory, *parsed, cppbor::UINT, "fused", kValidFused);
             error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "security_level",
                                    kValidSecurityLevels);
-            if (parsed->get("security_level") && parsed->get("security_level")->asTstr() &&
-                parsed->get("security_level")->asTstr()->value() == "tee") {
+            if (isTeeDeviceInfo(*parsed)) {
                 error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "os_version");
             }
             break;
@@ -570,7 +619,7 @@
     }
 
     // BCC is [ pubkey, + BccEntry]
-    auto bccContents = validateBcc(bcc->asArray(), hwtrust::DiceChain::Kind::kProtectedData);
+    auto bccContents = validateBcc(bcc->asArray(), hwtrust::DiceChain::Kind::kVsr13);
     if (!bccContents) {
         return bccContents.message() + "\n" + prettyPrint(bcc.get());
     }
@@ -861,7 +910,7 @@
     }
 
     // DICE chain is [ pubkey, + DiceChainEntry ].
-    auto diceContents = validateBcc(diceCertChain, hwtrust::DiceChain::Kind::kAuthenticatedMessage);
+    auto diceContents = validateBcc(diceCertChain, hwtrust::DiceChain::Kind::kVsr14);
     if (!diceContents) {
         return diceContents.message() + "\n" + prettyPrint(diceCertChain);
     }
diff --git a/security/rkp/CHANGELOG.md b/security/rkp/CHANGELOG.md
index 9409a6d..f425284 100644
--- a/security/rkp/CHANGELOG.md
+++ b/security/rkp/CHANGELOG.md
@@ -31,7 +31,7 @@
 * IRemotelyProvisionedComponent
   * The need for an EEK has been removed. There is no longer an encrypted portion of the CSR.
   * Keys for new CSR format must be generated with test mode set to false, effectively removing test
-    mode in the new CSR flow. Old behavior is kept unchanged for backwards compatibility.
+    mode in the new CSR flow.
   * The schema for the CSR itself has been significantly simplified, please see
     IRemotelyProvisionedComponent.aidl for more details. Notably,
     * the chain of signing, MACing, and encryption operations has been replaced with a single
diff --git a/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index 35b83dd..7960c7f 100644
--- a/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
+++ b/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -144,9 +144,9 @@
     byte[] generateEcdsaP256KeyPair(in boolean testMode, out MacedPublicKey macedPublicKey);
 
     /**
-     * This method can be removed in version 3 of the HAL. The header is kept around for
-     * backwards compatibility purposes. From v3, this method is allowed to raise a
-     * ServiceSpecificException with an error code of STATUS_REMOVED.
+     * This method has been deprecated since version 3 of the HAL. The header is kept around for
+     * backwards compatibility purposes. From v3, this method must raise a ServiceSpecificException
+     * with an error code of STATUS_REMOVED.
      *
      * For v1 and v2 implementations:
      * generateCertificateRequest creates a certificate request to be sent to the provisioning
diff --git a/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index bf40976..9f68bfa 100644
--- a/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -408,16 +408,8 @@
         ASSERT_FALSE(HasFatalFailure());
 
         if (rpcHardwareInfo.versionNumber >= VERSION_WITHOUT_TEST_MODE) {
-            bytevec keysToSignMac;
-            DeviceInfo deviceInfo;
-            ProtectedData protectedData;
-            auto status = provisionable_->generateCertificateRequest(
-                    false, {}, {}, {}, &deviceInfo, &protectedData, &keysToSignMac);
-            if (!status.isOk() && (status.getServiceSpecificError() ==
-                                   BnRemotelyProvisionedComponent::STATUS_REMOVED)) {
-                GTEST_SKIP() << "This test case applies to RKP v3+ only if "
-                             << "generateCertificateRequest() is implemented.";
-            }
+            GTEST_SKIP() << "This test case only applies to RKP v1 and v2. "
+                         << "RKP version discovered: " << rpcHardwareInfo.versionNumber;
         }
     }
 };
@@ -798,6 +790,20 @@
               BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
 }
 
+/**
+ * Call generateCertificateRequest(). Make sure it's removed.
+ */
+TEST_P(CertificateRequestV2Test, CertificateRequestV1Removed) {
+    bytevec keysToSignMac;
+    DeviceInfo deviceInfo;
+    ProtectedData protectedData;
+    auto status = provisionable_->generateCertificateRequest(
+            true /* testMode */, {} /* keysToSign */, {} /* EEK chain */, challenge_, &deviceInfo,
+            &protectedData, &keysToSignMac);
+    ASSERT_FALSE(status.isOk()) << status.getMessage();
+    EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_REMOVED);
+}
+
 void parse_root_of_trust(const vector<uint8_t>& attestation_cert,
                          vector<uint8_t>* verified_boot_key, VerifiedBoot* verified_boot_state,
                          bool* device_locked, vector<uint8_t>* verified_boot_hash) {
diff --git a/tests/extension/vibrator/aidl/Android.bp b/tests/extension/vibrator/aidl/Android.bp
index 20df5bb..0306dca 100644
--- a/tests/extension/vibrator/aidl/Android.bp
+++ b/tests/extension/vibrator/aidl/Android.bp
@@ -38,5 +38,11 @@
             enabled: false,
         },
     },
-    versions: ["1"],
+    frozen: true,
+    versions_with_info: [
+        {
+            version: "1",
+            imports: ["android.hardware.vibrator-V2"],
+        },
+    ],
 }
diff --git a/tests/extension/vibrator/aidl/default/Android.bp b/tests/extension/vibrator/aidl/default/Android.bp
index 0f3895f..5e156af 100644
--- a/tests/extension/vibrator/aidl/default/Android.bp
+++ b/tests/extension/vibrator/aidl/default/Android.bp
@@ -29,6 +29,6 @@
         "libbase",
         "libbinder_ndk",
         "android.hardware.vibrator-V2-ndk",
-        "android.hardware.tests.extension.vibrator-V2-ndk",
+        "android.hardware.tests.extension.vibrator-V1-ndk",
     ],
 }
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
index 8413f06..c3ac401 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
@@ -40,6 +40,7 @@
   CCC_UWB_CONFIG_ID = 164,
   CCC_PULSESHAPE_COMBO = 165,
   CCC_URSK_TTL = 166,
+  CCC_LAST_INDEX_USED = 168,
   NB_OF_RANGE_MEASUREMENTS = 227,
   NB_OF_AZIMUTH_MEASUREMENTS = 228,
   NB_OF_ELEVATION_MEASUREMENTS = 229,
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
index f303ed9..65bb1c9 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
@@ -46,6 +46,8 @@
     CCC_PULSESHAPE_COMBO = 0xA5,
     /** 2 byte data */
     CCC_URSK_TTL = 0xA6,
+    /** 4 byte data */
+    CCC_LAST_INDEX_USED = 0xA8,
 
     /**
      * Range 0xE3 - 0xFF is reserved for proprietary use in the FIRA spec.
diff --git a/wifi/1.6/default/Android.bp b/wifi/1.6/default/Android.bp
index 0f98e71..6038e36 100644
--- a/wifi/1.6/default/Android.bp
+++ b/wifi/1.6/default/Android.bp
@@ -26,6 +26,7 @@
         "hidl_feature_disable_ap", // WIFI_HIDL_FEATURE_DISABLE_AP
         "hidl_feature_disable_ap_mac_randomization", // WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION
         "avoid_iface_reset_mac_change", // WIFI_AVOID_IFACE_RESET_MAC_CHANGE
+        "wifi_skip_state_toggle_off_on_for_nan", // WIFI_SKIP_STATE_TOGGLE_OFF_ON_FOR_NAN
     ],
     value_variables: [
         "hal_interface_combinations", // WIFI_HAL_INTERFACE_COMBINATIONS
@@ -53,6 +54,9 @@
         avoid_iface_reset_mac_change: {
             cppflags: ["-DWIFI_AVOID_IFACE_RESET_MAC_CHANGE"],
         },
+        wifi_skip_state_toggle_off_on_for_nan: {
+            cppflags: ["-DWIFI_SKIP_STATE_TOGGLE_OFF_ON_FOR_NAN"],
+        },
         hal_interface_combinations: {
             cppflags: ["-DWIFI_HAL_INTERFACE_COMBINATIONS=%s"],
         },
diff --git a/wifi/1.6/default/wifi_nan_iface.cpp b/wifi/1.6/default/wifi_nan_iface.cpp
index ac2ebc9..4c61ba7 100644
--- a/wifi/1.6/default/wifi_nan_iface.cpp
+++ b/wifi/1.6/default/wifi_nan_iface.cpp
@@ -453,6 +453,7 @@
 
     // Register for iface state toggle events.
     iface_util::IfaceEventHandlers event_handlers = {};
+#ifndef WIFI_SKIP_STATE_TOGGLE_OFF_ON_FOR_NAN
     event_handlers.on_state_toggle_off_on = [weak_ptr_this](const std::string& /* iface_name */) {
         const auto shared_ptr_this = weak_ptr_this.promote();
         if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
@@ -467,6 +468,7 @@
             }
         }
     };
+#endif
     iface_util_.lock()->registerIfaceEventHandlers(ifname_, event_handlers);
 }
 
diff --git a/wifi/aidl/default/Android.bp b/wifi/aidl/default/Android.bp
index 441d461..91d609d 100644
--- a/wifi/aidl/default/Android.bp
+++ b/wifi/aidl/default/Android.bp
@@ -26,6 +26,7 @@
         "hidl_feature_disable_ap", // WIFI_HIDL_FEATURE_DISABLE_AP
         "hidl_feature_disable_ap_mac_randomization", // WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION
         "avoid_iface_reset_mac_change", // WIFI_AVOID_IFACE_RESET_MAC_CHANGE
+        "wifi_skip_state_toggle_off_on_for_nan", // WIFI_SKIP_STATE_TOGGLE_OFF_ON_FOR_NAN
     ],
     value_variables: [
         "hal_interface_combinations", // WIFI_HAL_INTERFACE_COMBINATIONS
@@ -53,6 +54,9 @@
         avoid_iface_reset_mac_change: {
             cppflags: ["-DWIFI_AVOID_IFACE_RESET_MAC_CHANGE"],
         },
+        wifi_skip_state_toggle_off_on_for_nan: {
+            cppflags: ["-DWIFI_SKIP_STATE_TOGGLE_OFF_ON_FOR_NAN"],
+        },
         hal_interface_combinations: {
             cppflags: ["-DWIFI_HAL_INTERFACE_COMBINATIONS=%s"],
         },
diff --git a/wifi/aidl/default/wifi_nan_iface.cpp b/wifi/aidl/default/wifi_nan_iface.cpp
index 8e3a191..cefe7f7 100644
--- a/wifi/aidl/default/wifi_nan_iface.cpp
+++ b/wifi/aidl/default/wifi_nan_iface.cpp
@@ -623,6 +623,7 @@
 
     // Register for iface state toggle events.
     iface_util::IfaceEventHandlers event_handlers = {};
+#ifndef WIFI_SKIP_STATE_TOGGLE_OFF_ON_FOR_NAN
     event_handlers.on_state_toggle_off_on = [weak_ptr_this](const std::string& /* iface_name */) {
         const auto shared_ptr_this = weak_ptr_this.lock();
         if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
@@ -637,6 +638,7 @@
             }
         }
     };
+#endif
     iface_util_.lock()->registerIfaceEventHandlers(ifname_, event_handlers);
 }
 
diff --git a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
index 07817cc..adc9b73 100644
--- a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
+++ b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
@@ -92,14 +92,21 @@
 }
 
 /*
- * GetApfPacketFilterCapabilities
+ * CheckApfIsSupported:
+ * Ensures the APF packet filter is fully supported as required in VSR 14:
+ * https://docs.partner.android.com/gms/policies/vsr/vsr-14
  */
-TEST_P(WifiStaIfaceAidlTest, GetApfPacketFilterCapabilities) {
-    if (!isFeatureSupported(IWifiStaIface::FeatureSetMask::APF)) {
-        GTEST_SKIP() << "APF packet filter capabilities are not supported.";
-    }
+TEST_P(WifiStaIfaceAidlTest, CheckApfIsSupported) {
+    // It is not required to check the vendor API level is at least U here
+    // because the Wi-Fi AIDL interface is launched with Android U(VSR-14).
+    // TODO: Add wavier list to the chipsets that doesn't support APF.
+    EXPECT_TRUE(isFeatureSupported(IWifiStaIface::FeatureSetMask::APF));
     StaApfPacketFilterCapabilities apf_caps = {};
     EXPECT_TRUE(wifi_sta_iface_->getApfPacketFilterCapabilities(&apf_caps).isOk());
+    // The APF version must be 4 and the usable memory must be at least
+    // 1024 bytes.
+    EXPECT_EQ(apf_caps.version, 4);
+    EXPECT_GE(apf_caps.maxLength, 1024);
 }
 
 /*
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl
index 2f9e528..2e11c76 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl
@@ -18,7 +18,8 @@
 
 /**
  * Callback to allow supplicant to retrieve non-standard certificate types
- * from the client.
+ * from the framework. Certificates can be stored in the framework using
+ * the WifiKeystore#put system API.
  *
  * Must be registered by the client at initialization, so that
  * supplicant can call into the client to retrieve any values.