Implement getSupportedValuesLists in DefaultVehicleHal.

Implement getSupportedValuesLists in DefaultVehicleHal. This CL
also updates the doc to allow per [propId, areaId] to still kept
HasSupportedValueInfo as null for new VHAL implementation. This allows
VHAL implementation to opt-in the new supported values API on a
per [propId, areaId] basis.

Flag: EXEMPT HAL impl
Test: atest DefaultVehicleHalTest
Bug: 382563296
Change-Id: I931fe18c2e326c46446033204e8fdf63d5c10c8a
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl
index 220b1da..96c4953 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl
@@ -267,6 +267,9 @@
     /**
      * Gets the supported values lists for [propId, areaId]s.
      *
+     * This is only supported for [propId, areaId]s that have non-null
+     * {@code hasSupportedValueInfo} for their {@code VehicleAreaConfig}.
+     *
      * For a specific [propId, areaId], if the hardware currently specifies
      * a list of supported values for it, then it is returned through the
      * {@code supportedValuesList} field inside
@@ -304,6 +307,9 @@
     /**
      * Gets the min/max supported values for [propId, areaId]s.
      *
+     * This is only supported for [propId, areaId]s that have non-null
+     * {@code hasSupportedValueInfo} for their {@code VehicleAreaConfig}.
+     *
      * For a specific [propId, areaId], if the hardware currently specifies
      * min/max supported value for it, then it is returned through the
      * {@code minSupportedValue} or {@code maxSupportedValue} field inside
@@ -342,6 +348,9 @@
     /**
      * Registers the supported value change callback.
      *
+     * This is only supported for [propId, areaId]s that have non-null
+     * {@code hasSupportedValueInfo} for their {@code VehicleAreaConfig}.
+     *
      * For the specified [propId, areaId]s,
      * {@code callback.onSupportedValueChange} must be invoked if change
      * happens.
@@ -360,6 +369,9 @@
     /**
      * Unregisters the supported value change callback.
      *
+     * This is only supported for [propId, areaId]s that have non-null
+     * {@code hasSupportedValueInfo} for their {@code VehicleAreaConfig}.
+     *
      * @param callback The callback to unregister.
      * @param propIdAreaIds A list of [propId, areaId]s to unregister.
      */
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.aidl
index f85ad3a..a3508ee 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.aidl
@@ -43,6 +43,8 @@
      *
      * If the [propId, areaId] does not specify a max supported value, this
      * is {@code null}.
+     *
+     * This must be ignored if status is not {@code StatusCode.OK}.
      */
     @nullable RawPropValues maxSupportedValue;
 }
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SupportedValuesListResult.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SupportedValuesListResult.aidl
index 4524f4f..8800b0b 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SupportedValuesListResult.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SupportedValuesListResult.aidl
@@ -36,6 +36,8 @@
      *
      * If the [propId, areaId] does not specify a supported values list, this
      * is {@code null}.
+     *
+     * This must be ignored if status is not {@code StatusCode.OK}.
      */
     @nullable List<RawPropValues> supportedValuesList;
 }
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
index 62d7e21..c6b8cd1 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
@@ -230,10 +230,17 @@
     boolean supportVariableUpdateRate;
 
     /**
-     * For VHAL implementation >= V4, this must not be {@code null}. This specifies whether
-     * this property may have min/max supported value or supported values list.
+     * This specifies whether this property may have min/max supported value or supported values
+     * list for [propId, areaId] that supports new supported values APIs.
      *
-     * For VHAL implementation < V4, this is always {@code null} and is not accessed.
+     * If this is not {@code null}. The client may use {@code getMinMaxSupportedValue},
+     * {@code getSupportedValuesLists}, {@code subscribeSupportedValueChange},
+     * {@code unsubscribeSupportedValueChange}.
+     *
+     * If this is {@code null} for legacy properties, the APIs mentioned before are not supported.
+     * Client must fallback to use static supported value information in {@code VehicleAreaConfig}.
+     *
+     * For VHAL implementation < V4, this is always {@code null}.
      */
     @nullable HasSupportedValueInfo hasSupportedValueInfo;
 }
diff --git a/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h b/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h
index 0684655..f46a1c8 100644
--- a/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h
@@ -18,9 +18,11 @@
 #define android_hardware_automotive_vehicle_aidl_impl_hardware_include_IVehicleHardware_H_
 
 #include <VehicleHalTypes.h>
+#include <VehicleUtils.h>
 
 #include <memory>
 #include <optional>
+#include <unordered_set>
 #include <vector>
 
 namespace android {
@@ -59,10 +61,23 @@
     using GetValuesCallback = std::function<void(std::vector<aidlvhal::GetValueResult>)>;
     using PropertyChangeCallback = std::function<void(std::vector<aidlvhal::VehiclePropValue>)>;
     using PropertySetErrorCallback = std::function<void(std::vector<SetValueErrorEvent>)>;
+    using SupportedValueChangeCallback = std::function<void(std::vector<PropIdAreaId>)>;
 
     virtual ~IVehicleHardware() = default;
 
     // Get all the property configs.
+    //
+    // Note that {@code VehicleAreaConfig.HasSupportedValueInfo} field is newly introduced in VHAL
+    // V4 to specify whether the [propertyId, areaId] has specified min/max supported value or
+    // supported values list.
+    //
+    // Since IVehicleHardware is designed to be backward compatible, this field can be set to null.
+    // If this field is set to null, VHAL client should fallback to use min/max supported value
+    // information in {@code VehicleAreaConfig} and {@code supportedEnumVaules} for enum properties.
+    //
+    // It is highly recommended to specify {@code VehicleAreaConfig.HasSupportedValueInfo} for new
+    // property implementations, even if the property does not specify supported values or the
+    // supported values are static.
     virtual std::vector<aidlvhal::VehiclePropConfig> getAllPropertyConfigs() const = 0;
 
     // Get the property configs for the specified propId. This is used for early-boot
@@ -240,6 +255,76 @@
                                                   [[maybe_unused]] float sampleRate) {
         return aidlvhal::StatusCode::OK;
     }
+
+    // Gets the min/max supported values for each of the specified [propId, areaId]s.
+    //
+    // The returned result may change dynamically.
+    //
+    // This is only called for [propId, areaId] that has
+    // {@code HasSupportedValueInfo.hasMinSupportedValue} or
+    // {@code HasSupportedValueInfo.hasMinSupportedValue} set to true.
+    //
+    // Client must implement (override) this function if at least one [propId, areaId]'s
+    // {@code HasSupportedValueInfo} is not null.
+    virtual std::vector<aidlvhal::MinMaxSupportedValueResult> getMinMaxSupportedValues(
+            [[maybe_unused]] const std::vector<PropIdAreaId>& propIdAreaIds) {
+        return {};
+    }
+
+    // Gets the supported values list for each of the specified [propId, areaId]s.
+    //
+    // The returned result may change dynamically.
+    //
+    // This is only called for [propId, areaId] that has
+    // {@code HasSupportedValueInfo.hasSupportedValuesList} set to true.
+    //
+    // Client must implement (override) this function if at least one [propId, areaId]'s
+    // {@code HasSupportedValueInfo} is not null.
+    virtual std::vector<aidlvhal::SupportedValuesListResult> getSupportedValuesLists(
+            [[maybe_unused]] const std::vector<PropIdAreaId>& propIdAreaIds) {
+        return {};
+    }
+
+    // Register a callback that would be called when the min/max supported value or supported
+    // values list change dynamically for propertyID returned from
+    // getPropertyIdsThatImplementGetSupportedValue
+    //
+    // This function must only be called once during initialization.
+    //
+    // Client must implement (override) this function if at least one [propId, areaId]'s
+    // {@code HasSupportedValueInfo} is not null.
+    virtual void registerSupportedValueChangeCallback(
+            [[maybe_unused]] std::unique_ptr<const SupportedValueChangeCallback> callback) {
+        // Do nothing.
+    }
+
+    // Subscribes to the min/max supported value or supported values list change for the specified
+    // [propId, areaId]s.
+    //
+    // If the propertyId's supported values are static, then must do nothing.
+    //
+    // This is only called for [propId, areaId] that has non-null {@code HasSupportedValueInfo}.
+    //
+    // Client must implement (override) this function if at least one [propId, areaId]'s
+    // {@code HasSupportedValueInfo} is not null.
+    virtual aidlvhal::StatusCode subscribeSupportedValueChange(
+            [[maybe_unused]] const std::vector<PropIdAreaId>& propIdAreaIds) {
+        return aidlvhal::StatusCode::OK;
+    }
+
+    // Unsubscrbies to the min/max supported value or supported values list change.
+    //
+    // Must do nothing if the [propId, areaId] was not previously subscribed to for supported
+    // values change.
+    //
+    // This is only called for [propId, areaId] that has non-null {@code HasSupportedValueInfo}.
+    //
+    // Client must implement (override) this function if at least one [propId, areaId]'s
+    // {@code HasSupportedValueInfo} is not null.
+    virtual aidlvhal::StatusCode unsubscribeSupportedValueChange(
+            [[maybe_unused]] const std::vector<PropIdAreaId>& propIdAreaIds) {
+        return aidlvhal::StatusCode::OK;
+    }
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/current/utils/common/include/VehicleHalTypes.h b/automotive/vehicle/aidl/impl/current/utils/common/include/VehicleHalTypes.h
index 4fa0a06..fcc006b 100644
--- a/automotive/vehicle/aidl/impl/current/utils/common/include/VehicleHalTypes.h
+++ b/automotive/vehicle/aidl/impl/current/utils/common/include/VehicleHalTypes.h
@@ -53,6 +53,8 @@
 #include <aidl/android/hardware/automotive/vehicle/LocationCharacterization.h>
 #include <aidl/android/hardware/automotive/vehicle/LowSpeedAutomaticEmergencyBrakingState.h>
 #include <aidl/android/hardware/automotive/vehicle/LowSpeedCollisionWarningState.h>
+#include <aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResult.h>
+#include <aidl/android/hardware/automotive/vehicle/MinMaxSupportedValueResults.h>
 #include <aidl/android/hardware/automotive/vehicle/Obd2CommonIgnitionMonitors.h>
 #include <aidl/android/hardware/automotive/vehicle/Obd2FuelSystemStatus.h>
 #include <aidl/android/hardware/automotive/vehicle/Obd2FuelType.h>
@@ -65,6 +67,8 @@
 #include <aidl/android/hardware/automotive/vehicle/SetValueResults.h>
 #include <aidl/android/hardware/automotive/vehicle/StatusCode.h>
 #include <aidl/android/hardware/automotive/vehicle/SubscribeOptions.h>
+#include <aidl/android/hardware/automotive/vehicle/SupportedValuesListResult.h>
+#include <aidl/android/hardware/automotive/vehicle/SupportedValuesListResults.h>
 #include <aidl/android/hardware/automotive/vehicle/VehicleAirbagLocation.h>
 #include <aidl/android/hardware/automotive/vehicle/VehicleApPowerBootupReason.h>
 #include <aidl/android/hardware/automotive/vehicle/VehicleApPowerStateReport.h>
diff --git a/automotive/vehicle/aidl/impl/current/vhal/include/DefaultVehicleHal.h b/automotive/vehicle/aidl/impl/current/vhal/include/DefaultVehicleHal.h
index 5d64e6f..8785bcd 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/include/DefaultVehicleHal.h
+++ b/automotive/vehicle/aidl/impl/current/vhal/include/DefaultVehicleHal.h
@@ -233,6 +233,10 @@
             std::function<void(const std::unordered_map<int32_t, aidlvhal::VehiclePropConfig>&)>
                     callback) const EXCLUDES(mConfigLock);
 
+    android::base::Result<const aidlvhal::VehicleAreaConfig*> getAreaConfigForPropIdAreaId(
+            int32_t propId, int32_t areaId) const;
+    android::base::Result<const aidlvhal::HasSupportedValueInfo*> getHasSupportedValueInfo(
+            int32_t propId, int32_t areaId) const;
     // Puts the property change events into a queue so that they can handled in batch.
     static void batchPropertyChangeEvent(
             const std::weak_ptr<ConcurrentQueue<aidlvhal::VehiclePropValue>>& batchedEventQueue,
diff --git a/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp
index 1e55a1a..3edd651 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp
@@ -49,6 +49,7 @@
 using ::aidl::android::hardware::automotive::vehicle::GetValueRequests;
 using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
 using ::aidl::android::hardware::automotive::vehicle::GetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::HasSupportedValueInfo;
 using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
 using ::aidl::android::hardware::automotive::vehicle::MinMaxSupportedValueResults;
 using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
@@ -57,6 +58,7 @@
 using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
 using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
+using ::aidl::android::hardware::automotive::vehicle::SupportedValuesListResult;
 using ::aidl::android::hardware::automotive::vehicle::SupportedValuesListResults;
 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
@@ -77,6 +79,11 @@
 using ::ndk::ScopedAStatus;
 using ::ndk::ScopedFileDescriptor;
 
+using VhalPropIdAreaId = ::aidl::android::hardware::automotive::vehicle::PropIdAreaId;
+
+#define propIdtoString(PROP_ID) \
+    aidl::android::hardware::automotive::vehicle::toString(static_cast<VehicleProperty>(PROP_ID))
+
 std::string toString(const std::unordered_set<int64_t>& values) {
     std::string str = "";
     for (auto it = values.begin(); it != values.end(); it++) {
@@ -964,30 +971,114 @@
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus DefaultVehicleHal::getSupportedValuesLists(
-        const std::vector<::aidl::android::hardware::automotive::vehicle::PropIdAreaId>&,
-        SupportedValuesListResults*) {
-    // TODO(b/381020465): Add relevant implementation.
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+Result<const VehicleAreaConfig*> DefaultVehicleHal::getAreaConfigForPropIdAreaId(
+        int32_t propId, int32_t areaId) const {
+    auto result = getConfig(propId);
+    if (!result.ok()) {
+        return Error() << "Failed to get property config for propertyId: " << propIdtoString(propId)
+                       << ", error: " << result.error();
+    }
+    const VehiclePropConfig& config = result.value();
+    const VehicleAreaConfig* areaConfig = getAreaConfig(propId, areaId, config);
+    if (areaConfig == nullptr) {
+        return Error() << "AreaId config not found for propertyId: " << propIdtoString(propId)
+                       << ", areaId: " << areaId;
+    }
+    return areaConfig;
 }
 
-ScopedAStatus DefaultVehicleHal::getMinMaxSupportedValue(
-        const std::vector<::aidl::android::hardware::automotive::vehicle::PropIdAreaId>&,
-        MinMaxSupportedValueResults*) {
+Result<const HasSupportedValueInfo*> DefaultVehicleHal::getHasSupportedValueInfo(
+        int32_t propId, int32_t areaId) const {
+    Result<const VehicleAreaConfig*> propIdAreaIdConfigResult =
+            getAreaConfigForPropIdAreaId(propId, areaId);
+    if (!isGlobalProp(propId) && !propIdAreaIdConfigResult.ok()) {
+        // For global property, it is possible that no config exists.
+        return Error() << propIdAreaIdConfigResult.error();
+    }
+    if (propIdAreaIdConfigResult.has_value()) {
+        auto areaConfig = propIdAreaIdConfigResult.value();
+        if (areaConfig->hasSupportedValueInfo.has_value()) {
+            return &(areaConfig->hasSupportedValueInfo.value());
+        }
+    }
+    return Error() << "property: " << propIdtoString(propId) << ", areaId: " << areaId
+                   << " does not support this operation because hasSupportedValueInfo is null";
+}
+
+ScopedAStatus DefaultVehicleHal::getSupportedValuesLists(
+        const std::vector<VhalPropIdAreaId>& vhalPropIdAreaIds,
+        SupportedValuesListResults* supportedValuesListResults) {
+    std::vector<size_t> toHardwareRequestCounters;
+    std::vector<PropIdAreaId> toHardwarePropIdAreaIds;
+    std::vector<SupportedValuesListResult> results;
+    results.resize(vhalPropIdAreaIds.size());
+    for (size_t requestCounter = 0; requestCounter < vhalPropIdAreaIds.size(); requestCounter++) {
+        const auto& vhalPropIdAreaId = vhalPropIdAreaIds.at(requestCounter);
+        int32_t propId = vhalPropIdAreaId.propId;
+        int32_t areaId = vhalPropIdAreaId.areaId;
+        auto hasSupportedValueInfoResult = getHasSupportedValueInfo(propId, areaId);
+        if (!hasSupportedValueInfoResult.ok()) {
+            ALOGE("getSupportedValuesLists: %s",
+                  hasSupportedValueInfoResult.error().message().c_str());
+            results[requestCounter] = SupportedValuesListResult{
+                    .status = StatusCode::INVALID_ARG, .supportedValuesList = std::nullopt};
+            continue;
+        }
+
+        const auto& hasSupportedValueInfo = *(hasSupportedValueInfoResult.value());
+        if (hasSupportedValueInfo.hasSupportedValuesList) {
+            toHardwarePropIdAreaIds.push_back(PropIdAreaId{.propId = propId, .areaId = areaId});
+            toHardwareRequestCounters.push_back(requestCounter);
+        } else {
+            results[requestCounter] = SupportedValuesListResult{
+                    .status = StatusCode::OK, .supportedValuesList = std::nullopt};
+            continue;
+        }
+    }
+    if (toHardwarePropIdAreaIds.size() != 0) {
+        std::vector<SupportedValuesListResult> resultsFromHardware =
+                mVehicleHardware->getSupportedValuesLists(toHardwarePropIdAreaIds);
+        // It is guaranteed that toHardwarePropIdAreaIds, toHardwareRequestCounters,
+        // resultsFromHardware have the same size.
+        if (resultsFromHardware.size() != toHardwareRequestCounters.size()) {
+            return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                    toInt(StatusCode::INTERNAL_ERROR),
+                    fmt::format(
+                            "getSupportedValuesLists: Unexpected results size from IVehicleHardware"
+                            ", got: {}, expect: {}",
+                            resultsFromHardware.size(), toHardwareRequestCounters.size())
+                            .c_str());
+        }
+        for (size_t i = 0; i < toHardwareRequestCounters.size(); i++) {
+            results[toHardwareRequestCounters[i]] = resultsFromHardware[i];
+        }
+    }
+    ScopedAStatus status =
+            vectorToStableLargeParcelable(std::move(results), supportedValuesListResults);
+    if (!status.isOk()) {
+        int statusCode = status.getServiceSpecificError();
+        ALOGE("getSupportedValuesLists: failed to marshal result into large parcelable, error: "
+              "%s, code: %d",
+              status.getMessage(), statusCode);
+        return status;
+    }
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus DefaultVehicleHal::getMinMaxSupportedValue(const std::vector<VhalPropIdAreaId>&,
+                                                         MinMaxSupportedValueResults*) {
     // TODO(b/381020465): Add relevant implementation.
     return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
 
 ScopedAStatus DefaultVehicleHal::registerSupportedValueChangeCallback(
-        const std::shared_ptr<IVehicleCallback>&,
-        const std::vector<::aidl::android::hardware::automotive::vehicle::PropIdAreaId>&) {
+        const std::shared_ptr<IVehicleCallback>&, const std::vector<VhalPropIdAreaId>&) {
     // TODO(b/381020465): Add relevant implementation.
     return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
 
 ScopedAStatus DefaultVehicleHal::unregisterSupportedValueChangeCallback(
-        const std::shared_ptr<IVehicleCallback>&,
-        const std::vector<::aidl::android::hardware::automotive::vehicle::PropIdAreaId>&) {
+        const std::shared_ptr<IVehicleCallback>&, const std::vector<VhalPropIdAreaId>&) {
     // TODO(b/381020465): Add relevant implementation.
     return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
diff --git a/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp
index ad34a4c..50bd8e0 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp
@@ -21,6 +21,7 @@
 
 #include <IVehicleHardware.h>
 #include <LargeParcelableBase.h>
+#include <aidl/android/hardware/automotive/vehicle/HasSupportedValueInfo.h>
 #include <aidl/android/hardware/automotive/vehicle/IVehicle.h>
 #include <aidl/android/hardware/automotive/vehicle/IVehicleCallback.h>
 
@@ -51,14 +52,18 @@
 using ::aidl::android::hardware::automotive::vehicle::GetValueRequests;
 using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
 using ::aidl::android::hardware::automotive::vehicle::GetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::HasSupportedValueInfo;
 using ::aidl::android::hardware::automotive::vehicle::IVehicle;
 using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
+using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
 using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
 using ::aidl::android::hardware::automotive::vehicle::SetValueRequests;
 using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
 using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
 using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
+using ::aidl::android::hardware::automotive::vehicle::SupportedValuesListResult;
+using ::aidl::android::hardware::automotive::vehicle::SupportedValuesListResults;
 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWindow;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
@@ -85,6 +90,8 @@
 using ::testing::UnorderedElementsAreArray;
 using ::testing::WhenSortedBy;
 
+using VhalPropIdAreaId = ::aidl::android::hardware::automotive::vehicle::PropIdAreaId;
+
 constexpr int32_t INVALID_PROP_ID = 0;
 // VehiclePropertyGroup:VENDOR,VehicleArea:WINDOW,VehiclePropertyType:INT32
 constexpr int32_t INT32_WINDOW_PROP = 10001 + 0x20000000 + 0x03000000 + 0x00400000;
@@ -112,6 +119,11 @@
     return static_cast<int32_t>(i) + 0x20000000 + 0x01000000 + 0x00410000;
 }
 
+int32_t testInt32VecWindowProp(size_t i) {
+    // VehiclePropertyGroup:VENDOR,VehicleArea:WINDOW,VehiclePropertyType:INT32_VEC
+    return static_cast<int32_t>(i) + 0x20000000 + 0x03000000 + 0x00410000;
+}
+
 std::string toString(const std::vector<SubscribeOptions>& options) {
     std::string optionsStr;
     for (const auto& option : options) {
@@ -2121,6 +2133,144 @@
     }
 }
 
+TEST_F(DefaultVehicleHalTest, testGetSupportedValuesLists) {
+    auto testConfigs = std::vector<VehiclePropConfig>(
+            {// This ia valid request, but no supported values are specified.
+             VehiclePropConfig{
+                     .prop = testInt32VecProp(1),
+                     .areaConfigs =
+                             {
+                                     {.areaId = 0,
+                                      .hasSupportedValueInfo =
+                                              HasSupportedValueInfo{
+                                                      .hasSupportedValuesList = false,
+                                              }},
+                             },
+             },
+             // This is an invalid request since hasSupportedValueInfo is null. This is not
+             // supported.
+             VehiclePropConfig{
+                     .prop = testInt32VecWindowProp(2),
+                     .areaConfigs =
+                             {
+                                     {
+                                             .areaId = 2,
+                                     },
+                             },
+             },
+             // This is an invalid request for global property.
+             VehiclePropConfig{
+                     .prop = testInt32VecProp(3),
+             },
+             // This is a normal request.
+             VehiclePropConfig{
+                     .prop = testInt32VecWindowProp(4),
+                     .areaConfigs =
+                             {
+                                     {.areaId = 4,
+                                      .hasSupportedValueInfo =
+                                              HasSupportedValueInfo{
+                                                      .hasSupportedValuesList = true,
+                                              }},
+                             },
+             }});
+
+    auto hardware = std::make_unique<MockVehicleHardware>();
+    MockVehicleHardware* hardwarePtr = hardware.get();
+    hardware->setPropertyConfigs(testConfigs);
+
+    SupportedValuesListResult resultFromHardware = {
+            .status = StatusCode::OK,
+            .supportedValuesList =
+                    std::vector<std::optional<RawPropValues>>{RawPropValues{.int32Values = {1}}}};
+    auto response = std::vector<SupportedValuesListResult>({resultFromHardware});
+    hardware->setSupportedValuesListResponse(response);
+
+    auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+    std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+    SupportedValuesListResults results;
+
+    auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecProp(1), .areaId = 0};
+    auto propIdAreaId2 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2};
+    auto propIdAreaId3 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(3), .areaId = 0};
+    auto propIdAreaId4 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(4), .areaId = 4};
+    auto status = vhal->getSupportedValuesLists(
+            std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2, propIdAreaId3,
+                                          propIdAreaId4},
+            &results);
+
+    ASSERT_TRUE(status.isOk()) << "Get non-okay status from getSupportedValuesLists"
+                               << status.getMessage();
+    ASSERT_THAT(hardwarePtr->getSupportedValuesListRequest(),
+                ElementsAre(PropIdAreaId{.propId = testInt32VecWindowProp(4), .areaId = 4}))
+            << "Only valid request 4 should get to hardware";
+
+    ASSERT_EQ(results.payloads.size(), 4u);
+    SupportedValuesListResult result = results.payloads[0];
+    ASSERT_EQ(result.status, StatusCode::OK)
+            << "Must return OK even if the supported values list is not specified";
+    ASSERT_FALSE(result.supportedValuesList.has_value())
+            << "Must return an empty supported values list if not specified";
+
+    result = results.payloads[1];
+    ASSERT_EQ(result.status, StatusCode::INVALID_ARG)
+            << "PropId, areaId that set hasSupportedValueInfo to null must not be supported";
+    ASSERT_FALSE(result.supportedValuesList.has_value());
+
+    result = results.payloads[2];
+    ASSERT_EQ(result.status, StatusCode::INVALID_ARG)
+            << "PropId, areaId that set hasSupportedValueInfo to null must not be supported";
+    ASSERT_FALSE(result.supportedValuesList.has_value());
+
+    result = results.payloads[3];
+    ASSERT_EQ(result.status, StatusCode::OK);
+    ASSERT_TRUE(result.supportedValuesList.has_value());
+    ASSERT_EQ(result.supportedValuesList.value().size(), 1u);
+    ASSERT_EQ(result.supportedValuesList.value()[0]->int32Values.size(), 1u);
+    ASSERT_EQ((result.supportedValuesList.value())[0]->int32Values[0], 1);
+}
+
+TEST_F(DefaultVehicleHalTest, testGetSupportedValuesLists_propIdAreaIdNotFound) {
+    auto testConfigs = std::vector<VehiclePropConfig>({
+            VehiclePropConfig{
+                    .prop = testInt32VecWindowProp(1),
+                    .areaConfigs =
+                            {
+                                    {.areaId = 1,
+                                     .hasSupportedValueInfo =
+                                             HasSupportedValueInfo{
+                                                     .hasSupportedValuesList = true,
+                                             }},
+                            },
+            },
+    });
+
+    auto hardware = std::make_unique<MockVehicleHardware>();
+    hardware->setPropertyConfigs(testConfigs);
+
+    auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+    std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+    SupportedValuesListResults results;
+
+    // propId not valid.
+    auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 1};
+    // areaId not valid.
+    auto propIdAreaId2 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(1), .areaId = 2};
+
+    auto status = vhal->getSupportedValuesLists(
+            std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2}, &results);
+
+    ASSERT_TRUE(status.isOk()) << "Get non-okay status from getSupportedValuesLists"
+                               << status.getMessage();
+    ASSERT_EQ(results.payloads.size(), 2u);
+    SupportedValuesListResult result = results.payloads[0];
+    ASSERT_EQ(result.status, StatusCode::INVALID_ARG);
+    result = results.payloads[1];
+    ASSERT_EQ(result.status, StatusCode::INVALID_ARG);
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp
index e796ce5..ed5f47c 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp
@@ -30,6 +30,7 @@
 using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
 using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
+using ::aidl::android::hardware::automotive::vehicle::SupportedValuesListResult;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 
@@ -200,6 +201,13 @@
     return propIdAreaIds;
 }
 
+std::vector<SupportedValuesListResult> MockVehicleHardware::getSupportedValuesLists(
+        const std::vector<PropIdAreaId>& propIdAreaIds) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mSupportedValuesListRequest = propIdAreaIds;
+    return mSupportedValuesListResponse;
+}
+
 void MockVehicleHardware::registerOnPropertyChangeEvent(
         std::unique_ptr<const PropertyChangeCallback> callback) {
     std::scoped_lock<std::mutex> lockGuard(mLock);
@@ -267,6 +275,17 @@
     mEventBatchingWindow = window;
 }
 
+void MockVehicleHardware::setSupportedValuesListResponse(
+        const std::vector<SupportedValuesListResult>& response) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mSupportedValuesListResponse = response;
+}
+
+std::vector<PropIdAreaId> MockVehicleHardware::getSupportedValuesListRequest() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return mSupportedValuesListRequest;
+}
+
 std::chrono::nanoseconds MockVehicleHardware::getPropertyOnChangeEventBatchingWindow() {
     std::scoped_lock<std::mutex> lockGuard(mLock);
     return mEventBatchingWindow;
diff --git a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h
index 06e01a8..4b2ca7c 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h
@@ -67,6 +67,8 @@
     aidl::android::hardware::automotive::vehicle::StatusCode unsubscribe(int32_t propId,
                                                                          int32_t areaId) override;
     std::chrono::nanoseconds getPropertyOnChangeEventBatchingWindow() override;
+    std::vector<aidl::android::hardware::automotive::vehicle::SupportedValuesListResult>
+    getSupportedValuesLists(const std::vector<PropIdAreaId>& propIdAreaIds) override;
 
     // Test functions.
     void setPropertyConfigs(
@@ -78,6 +80,10 @@
     void addSetValueResponses(
             const std::vector<aidl::android::hardware::automotive::vehicle::SetValueResult>&
                     responses);
+    void setSupportedValuesListResponse(
+            const std::vector<
+                    aidl::android::hardware::automotive::vehicle::SupportedValuesListResult>&
+                    response);
     void setGetValueResponder(
             std::function<aidl::android::hardware::automotive::vehicle::StatusCode(
                     std::shared_ptr<const GetValuesCallback>,
@@ -88,6 +94,7 @@
     nextGetValueRequests();
     std::vector<aidl::android::hardware::automotive::vehicle::SetValueRequest>
     nextSetValueRequests();
+    std::vector<PropIdAreaId> getSupportedValuesListRequest();
     void setStatus(const char* functionName,
                    aidl::android::hardware::automotive::vehicle::StatusCode status);
     void setSleepTime(int64_t timeInNano);
@@ -118,6 +125,9 @@
             mSetValueRequests GUARDED_BY(mLock);
     mutable std::list<std::vector<aidl::android::hardware::automotive::vehicle::SetValueResult>>
             mSetValueResponses GUARDED_BY(mLock);
+    mutable std::vector<PropIdAreaId> mSupportedValuesListRequest GUARDED_BY(mLock);
+    mutable std::vector<aidl::android::hardware::automotive::vehicle::SupportedValuesListResult>
+            mSupportedValuesListResponse GUARDED_BY(mLock);
     std::unordered_map<const char*, aidl::android::hardware::automotive::vehicle::StatusCode>
             mStatusByFunctions GUARDED_BY(mLock);
     int64_t mSleepTime GUARDED_BY(mLock) = 0;