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 718f68ef5b..26fdee6372 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h @@ -222,6 +222,9 @@ class FakeVehicleHardware : public IVehicleHardware { const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const; bool isHvacPropAndHvacNotAvailable(int32_t propId, int32_t areaId) const; VhalResult isAdasPropertyAvailable(int32_t adasStatePropertyId) const; + VhalResult synchronizeHvacTemp(int32_t hvacDualOnAreaId, + std::optional newTempC) const; + std::optional getSyncedAreaIdIfHvacDualOn(int32_t hvacTemperatureSetAreaId) const; std::unordered_map loadConfigDeclarations(); 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 cb8e51f020..7ff03c67fb 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp @@ -601,6 +601,65 @@ VhalResult FakeVehicleHardware::setUserHalProp(const VehiclePropValue& val return {}; } +VhalResult FakeVehicleHardware::synchronizeHvacTemp(int32_t hvacDualOnAreaId, + std::optional newTempC) const { + auto hvacTemperatureSetResults = mServerSidePropStore->readValuesForProperty( + toInt(VehicleProperty::HVAC_TEMPERATURE_SET)); + if (!hvacTemperatureSetResults.ok()) { + return StatusError(StatusCode::NOT_AVAILABLE) + << "Failed to get HVAC_TEMPERATURE_SET, error: " + << getErrorMsg(hvacTemperatureSetResults); + } + auto& hvacTemperatureSetValues = hvacTemperatureSetResults.value(); + std::optional tempCToSynchronize = newTempC; + for (size_t i = 0; i < hvacTemperatureSetValues.size(); i++) { + int32_t areaId = hvacTemperatureSetValues[i]->areaId; + if ((hvacDualOnAreaId & areaId) != areaId) { + continue; + } + if (hvacTemperatureSetValues[i]->status != VehiclePropertyStatus::AVAILABLE) { + continue; + } + // When HVAC_DUAL_ON is initially enabled, synchronize all area IDs + // to the temperature of the first area ID, which is the driver's. + if (!tempCToSynchronize.has_value()) { + tempCToSynchronize = hvacTemperatureSetValues[i]->value.floatValues[0]; + continue; + } + auto updatedValue = std::move(hvacTemperatureSetValues[i]); + updatedValue->value.floatValues[0] = tempCToSynchronize.value(); + updatedValue->timestamp = elapsedRealtimeNano(); + // This will trigger a property change event for the current hvac property value. + auto writeResult = + mServerSidePropStore->writeValue(std::move(updatedValue), /*updateStatus=*/true, + VehiclePropertyStore::EventMode::ALWAYS); + if (!writeResult.ok()) { + return StatusError(getErrorCode(writeResult)) + << "Failed to write value into property store, error: " + << getErrorMsg(writeResult); + } + } + return {}; +} + +std::optional FakeVehicleHardware::getSyncedAreaIdIfHvacDualOn( + int32_t hvacTemperatureSetAreaId) const { + auto hvacDualOnResults = + mServerSidePropStore->readValuesForProperty(toInt(VehicleProperty::HVAC_DUAL_ON)); + if (!hvacDualOnResults.ok()) { + return std::nullopt; + } + auto& hvacDualOnValues = hvacDualOnResults.value(); + for (size_t i = 0; i < hvacDualOnValues.size(); i++) { + if ((hvacDualOnValues[i]->areaId & hvacTemperatureSetAreaId) == hvacTemperatureSetAreaId && + hvacDualOnValues[i]->value.int32Values.size() == 1 && + hvacDualOnValues[i]->value.int32Values[0] == 1) { + return hvacDualOnValues[i]->areaId; + } + } + return std::nullopt; +} + FakeVehicleHardware::ValueResultType FakeVehicleHardware::getUserHalProp( const VehiclePropValue& value) const { auto propId = value.prop; @@ -853,6 +912,28 @@ VhalResult FakeVehicleHardware::maybeSetSpecialValue(const VehiclePropValu case toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION): *isSpecialValue = true; return setHvacTemperatureValueSuggestion(value); + case toInt(VehicleProperty::HVAC_TEMPERATURE_SET): + if (value.value.floatValues.size() != 1) { + *isSpecialValue = true; + return StatusError(StatusCode::INVALID_ARG) + << "HVAC_DUAL_ON requires only one float value"; + } + if (auto hvacDualOnAreaId = getSyncedAreaIdIfHvacDualOn(value.areaId); + hvacDualOnAreaId.has_value()) { + *isSpecialValue = true; + return synchronizeHvacTemp(hvacDualOnAreaId.value(), value.value.floatValues[0]); + } + return {}; + case toInt(VehicleProperty::HVAC_DUAL_ON): + if (value.value.int32Values.size() != 1) { + *isSpecialValue = true; + return StatusError(StatusCode::INVALID_ARG) + << "HVAC_DUAL_ON requires only one int32 value"; + } + if (value.value.int32Values[0] == 1) { + synchronizeHvacTemp(value.areaId, std::nullopt); + } + return {}; case toInt(VehicleProperty::LANE_CENTERING_ASSIST_COMMAND): { isAdasPropertyAvailableResult = isAdasPropertyAvailable(toInt(VehicleProperty::LANE_CENTERING_ASSIST_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 0432500c4b..3b6f717fd2 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp @@ -1889,6 +1889,101 @@ TEST_F(FakeVehicleHardwareTest, testHvacPowerOnSendCurrentHvacPropValues) { } } +TEST_F(FakeVehicleHardwareTest, testHvacDualOnSynchronizesTemp) { + auto hvacDualOnConfig = std::move(getVehiclePropConfig(toInt(VehicleProperty::HVAC_DUAL_ON))); + auto hvacTemperatureSetConfig = + std::move(getVehiclePropConfig(toInt(VehicleProperty::HVAC_TEMPERATURE_SET))); + EXPECT_NE(hvacDualOnConfig, nullptr); + EXPECT_NE(hvacTemperatureSetConfig, nullptr); + for (auto& hvacTemperatureSetConfig : hvacTemperatureSetConfig->areaConfigs) { + int32_t hvacTemperatureSetAreaId = hvacTemperatureSetConfig.areaId; + subscribe(toInt(VehicleProperty::HVAC_TEMPERATURE_SET), hvacTemperatureSetAreaId, + /*sampleRateHz*/ 0); + } + for (auto& hvacDualOnConfig : hvacDualOnConfig->areaConfigs) { + int32_t hvacDualOnAreaId = hvacDualOnConfig.areaId; + subscribe(toInt(VehicleProperty::HVAC_DUAL_ON), hvacDualOnAreaId, /*sampleRateHz*/ 0); + StatusCode status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_DUAL_ON), + .areaId = hvacDualOnAreaId, + .value.int32Values = {1}}); + EXPECT_EQ(status, StatusCode::OK); + + // Verify there's an event for all HVAC_TEMPERATURE_SET + // area IDs covered by the HVAC_DUAL_ON area ID + auto events = getChangedProperties(); + std::unordered_set temperatureValues; + for (const auto& event : events) { + // Ignore HVAC_DUAL_ON event + if (event.prop == toInt(VehicleProperty::HVAC_DUAL_ON)) { + continue; + } + EXPECT_EQ(event.prop, toInt(VehicleProperty::HVAC_TEMPERATURE_SET)); + EXPECT_EQ((hvacDualOnAreaId & event.areaId), event.areaId); + EXPECT_EQ(1u, event.value.floatValues.size()); + temperatureValues.insert(event.value.floatValues[0]); + } + // Verify that the temperature value is the same for all events + // Ie the temperature in all area IDs are synchronized + EXPECT_EQ(1u, temperatureValues.size()); + clearChangedProperties(); + + // Verify when any HVAC_TEMPERATURE_SET area ID is changed all + // area IDs covered by the HVAC_DUAL_ON area ID are also changed + for (auto& hvacTemperatureSetConfig : hvacTemperatureSetConfig->areaConfigs) { + int32_t hvacTemperatureSetAreaId = hvacTemperatureSetConfig.areaId; + if ((hvacDualOnAreaId & hvacTemperatureSetAreaId) != hvacTemperatureSetAreaId) { + continue; + } + float expectedValue = 25; + status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_SET), + .areaId = hvacTemperatureSetAreaId, + .value.floatValues = {expectedValue}}); + EXPECT_EQ(status, StatusCode::OK); + events = getChangedProperties(); + for (const auto& event : events) { + EXPECT_EQ(event.prop, toInt(VehicleProperty::HVAC_TEMPERATURE_SET)); + EXPECT_EQ(1u, event.value.floatValues.size()); + EXPECT_EQ(expectedValue, event.value.floatValues[0]); + } + clearChangedProperties(); + } + + status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_DUAL_ON), + .areaId = hvacDualOnAreaId, + .value.int32Values = {0}}); + EXPECT_EQ(status, StatusCode::OK); + + // When HVAC_DUAL_ON is disabled, there should be no events created + // for HVAC_TEMPERATURE_SET ie no temperature synchronization. + events = getChangedProperties(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(events[0].prop, toInt(VehicleProperty::HVAC_DUAL_ON)); + EXPECT_EQ(events[0].areaId, hvacDualOnAreaId); + clearChangedProperties(); + + // Verify when any HVAC_TEMPERATURE_SET area ID is + // changed other area IDs do not change. + for (auto& hvacTemperatureSetConfig : hvacTemperatureSetConfig->areaConfigs) { + int32_t hvacTemperatureSetAreaId = hvacTemperatureSetConfig.areaId; + if ((hvacDualOnAreaId & hvacTemperatureSetAreaId) != hvacTemperatureSetAreaId) { + continue; + } + float expectedValue = 24; + status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_SET), + .areaId = hvacTemperatureSetAreaId, + .value.floatValues = {expectedValue}}); + EXPECT_EQ(status, StatusCode::OK); + events = getChangedProperties(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(events[0].prop, toInt(VehicleProperty::HVAC_TEMPERATURE_SET)); + EXPECT_EQ(events[0].areaId, hvacTemperatureSetAreaId); + EXPECT_EQ(1u, events[0].value.floatValues.size()); + EXPECT_EQ(expectedValue, events[0].value.floatValues[0]); + clearChangedProperties(); + } + } +} + TEST_F(FakeVehicleHardwareTest, testGetAdasPropNotAvailable) { std::unordered_map> adasEnabledPropToDependentProps = { {