Merge "Implement HVAC_TEMPERATURE_VALUE_SUGGESTION in emulator VHAL" into udc-dev
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