1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertFalse; 23 import static org.junit.Assert.assertThrows; 24 import static org.junit.Assert.assertTrue; 25 26 import android.car.Car; 27 import android.car.hardware.CarPropertyValue; 28 import android.car.hardware.hvac.CarHvacManager; 29 import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback; 30 import android.car.hardware.hvac.CarHvacManager.PropertyId; 31 import android.hardware.automotive.vehicle.VehicleAreaSeat; 32 import android.hardware.automotive.vehicle.VehicleAreaWindow; 33 import android.hardware.automotive.vehicle.VehiclePropValue; 34 import android.hardware.automotive.vehicle.VehicleProperty; 35 import android.hardware.automotive.vehicle.VehiclePropertyAccess; 36 import android.hardware.automotive.vehicle.VehiclePropertyChangeMode; 37 import android.os.SystemClock; 38 import android.util.Log; 39 import android.util.MutableInt; 40 41 import androidx.test.ext.junit.runners.AndroidJUnit4; 42 import androidx.test.filters.MediumTest; 43 44 import com.android.car.hal.test.AidlMockedVehicleHal.VehicleHalPropertyHandler; 45 import com.android.car.hal.test.AidlVehiclePropValueBuilder; 46 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 50 import java.util.HashMap; 51 import java.util.concurrent.CountDownLatch; 52 import java.util.concurrent.Semaphore; 53 import java.util.concurrent.TimeUnit; 54 55 @RunWith(AndroidJUnit4.class) 56 @MediumTest 57 public class CarHvacManagerTest extends MockedCarTestBase { 58 private static final String TAG = CarHvacManagerTest.class.getSimpleName(); 59 60 // Use this semaphore to block until the callback is heard of. 61 private Semaphore mAvailable; 62 63 private CarHvacManager mCarHvacManager; 64 private boolean mEventBoolVal; 65 private float mEventFloatVal; 66 private int mEventIntVal; 67 private int mEventZoneVal; 68 private final HvacPropertyHandler mHandler = new HvacPropertyHandler(); 69 70 @Override configureMockedHal()71 protected void configureMockedHal() { 72 addAidlProperty(VehicleProperty.HVAC_DEFROSTER, mHandler) 73 .addAreaConfig(VehicleAreaWindow.FRONT_WINDSHIELD, 0, 0); 74 addAidlProperty(VehicleProperty.HVAC_FAN_SPEED, mHandler) 75 .addAreaConfig(VehicleAreaSeat.ROW_1_LEFT, 0, 0); 76 addAidlProperty(VehicleProperty.HVAC_TEMPERATURE_SET, mHandler) 77 .addAreaConfig(VehicleAreaSeat.ROW_1_LEFT, 0, 0); 78 addAidlProperty(VehicleProperty.HVAC_TEMPERATURE_CURRENT, mHandler) 79 .setChangeMode(VehiclePropertyChangeMode.CONTINUOUS) 80 .setAccess(VehiclePropertyAccess.READ) 81 .addAreaConfig(VehicleAreaSeat.ROW_1_LEFT | VehicleAreaSeat.ROW_1_RIGHT, 0, 0); 82 addAidlProperty(VehicleProperty.HVAC_AC_ON, mHandler) 83 .addAreaConfig(VehicleAreaSeat.ROW_1_CENTER); 84 } 85 86 @Override setUp()87 public void setUp() throws Exception { 88 super.setUp(); 89 mAvailable = new Semaphore(0); 90 mCarHvacManager = (CarHvacManager) getCar().getCarManager(Car.HVAC_SERVICE); 91 mCarHvacManager.setIntProperty(VehicleProperty.HVAC_FAN_SPEED, 92 VehicleAreaSeat.ROW_1_LEFT, 0); 93 } 94 95 // Test a boolean property 96 @Test testHvacRearDefrosterOn()97 public void testHvacRearDefrosterOn() throws Exception { 98 mCarHvacManager.setBooleanProperty(CarHvacManager.ID_WINDOW_DEFROSTER_ON, 99 VehicleAreaWindow.FRONT_WINDSHIELD, true); 100 boolean defrost = mCarHvacManager.getBooleanProperty(CarHvacManager.ID_WINDOW_DEFROSTER_ON, 101 VehicleAreaWindow.FRONT_WINDSHIELD); 102 assertTrue(defrost); 103 104 mCarHvacManager.setBooleanProperty(CarHvacManager.ID_WINDOW_DEFROSTER_ON, 105 VehicleAreaWindow.FRONT_WINDSHIELD, false); 106 defrost = mCarHvacManager.getBooleanProperty(CarHvacManager.ID_WINDOW_DEFROSTER_ON, 107 VehicleAreaWindow.FRONT_WINDSHIELD); 108 assertFalse(defrost); 109 } 110 111 /** 112 * Test {@link CarHvacManager#isPropertyAvailable(int, int)} 113 */ 114 @Test testHvacPropertyAvailable()115 public void testHvacPropertyAvailable() { 116 assertThat(mCarHvacManager.isPropertyAvailable(VehicleProperty.HVAC_AC_ON, 117 VehicleAreaSeat.ROW_1_CENTER)).isFalse(); 118 assertThat(mCarHvacManager.isPropertyAvailable(VehicleProperty.HVAC_FAN_SPEED, 119 VehicleAreaSeat.ROW_1_LEFT)).isTrue(); 120 } 121 122 // Test an integer property 123 @Test testHvacFanSpeed()124 public void testHvacFanSpeed() throws Exception { 125 mCarHvacManager.setIntProperty(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, 126 VehicleAreaSeat.ROW_1_LEFT, 15); 127 int speed = mCarHvacManager.getIntProperty(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, 128 VehicleAreaSeat.ROW_1_LEFT); 129 assertEquals(15, speed); 130 131 mCarHvacManager.setIntProperty(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, 132 VehicleAreaSeat.ROW_1_LEFT, 23); 133 speed = mCarHvacManager.getIntProperty(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, 134 VehicleAreaSeat.ROW_1_LEFT); 135 assertEquals(23, speed); 136 } 137 138 // Test an float property 139 @Test testHvacTempSetpoint()140 public void testHvacTempSetpoint() throws Exception { 141 mCarHvacManager.setFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT, 142 VehicleAreaSeat.ROW_1_LEFT, 70); 143 float temp = mCarHvacManager.getFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT, 144 VehicleAreaSeat.ROW_1_LEFT); 145 assertEquals(70.0, temp, 0); 146 147 mCarHvacManager.setFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT, 148 VehicleAreaSeat.ROW_1_LEFT, (float) 65.5); 149 temp = mCarHvacManager.getFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT, 150 VehicleAreaSeat.ROW_1_LEFT); 151 assertEquals(65.5, temp, 0); 152 } 153 154 @Test testError()155 public void testError() throws Exception { 156 final int PROP = VehicleProperty.HVAC_DEFROSTER; 157 final int AREA = VehicleAreaWindow.FRONT_WINDSHIELD; 158 final int ERR_CODE = 42; 159 160 CountDownLatch errorLatch = new CountDownLatch(1); 161 MutableInt propertyIdReceived = new MutableInt(0); 162 MutableInt areaIdReceived = new MutableInt(0); 163 164 mCarHvacManager.registerCallback(new CarHvacEventCallback() { 165 @Override 166 public void onChangeEvent(CarPropertyValue value) { 167 168 } 169 170 @Override 171 public void onErrorEvent(@PropertyId int propertyId, int area) { 172 propertyIdReceived.value = propertyId; 173 areaIdReceived.value = area; 174 errorLatch.countDown(); 175 } 176 }); 177 mCarHvacManager.setBooleanProperty(PROP, AREA, true); 178 getAidlMockedVehicleHal().injectError(ERR_CODE, PROP, AREA); 179 assertTrue(errorLatch.await(DEFAULT_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS)); 180 assertEquals(PROP, propertyIdReceived.value); 181 assertEquals(AREA, areaIdReceived.value); 182 } 183 184 // Test an event 185 @Test testEvent()186 public void testEvent() throws Exception { 187 mCarHvacManager.registerCallback(new EventListener()); 188 // Wait for events generated on registration 189 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 190 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 191 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 192 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 193 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 194 195 // Inject a boolean event and wait for its callback in onPropertySet. 196 VehiclePropValue v = AidlVehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_DEFROSTER) 197 .setAreaId(VehicleAreaWindow.FRONT_WINDSHIELD) 198 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 199 .addIntValues(1) 200 .build(); 201 assertEquals(0, mAvailable.availablePermits()); 202 getAidlMockedVehicleHal().injectEvent(v); 203 204 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 205 assertTrue(mEventBoolVal); 206 assertEquals(mEventZoneVal, VehicleAreaWindow.FRONT_WINDSHIELD); 207 208 // Inject a float event and wait for its callback in onPropertySet. 209 v = AidlVehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_TEMPERATURE_CURRENT) 210 .setAreaId(VehicleAreaSeat.ROW_1_LEFT | VehicleAreaSeat.ROW_1_RIGHT) 211 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 212 .addFloatValues(67f) 213 .build(); 214 assertEquals(0, mAvailable.availablePermits()); 215 getAidlMockedVehicleHal().injectEvent(v); 216 217 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 218 assertEquals(67, mEventFloatVal, 0); 219 assertEquals(VehicleAreaSeat.ROW_1_LEFT | VehicleAreaSeat.ROW_1_RIGHT, mEventZoneVal); 220 221 // Inject an integer event and wait for its callback in onPropertySet. 222 v = AidlVehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_FAN_SPEED) 223 .setAreaId(VehicleAreaSeat.ROW_1_LEFT) 224 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 225 .addIntValues(4) 226 .build(); 227 assertEquals(0, mAvailable.availablePermits()); 228 getAidlMockedVehicleHal().injectEvent(v); 229 230 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 231 assertEquals(4, mEventIntVal); 232 assertEquals(VehicleAreaSeat.ROW_1_LEFT, mEventZoneVal); 233 } 234 235 /** 236 * Test {@link CarHvacManager#unregisterCallback(CarHvacEventCallback)} 237 */ 238 @Test testUnregisterCallback()239 public void testUnregisterCallback() throws Exception { 240 EventListener listener = new EventListener(); 241 mCarHvacManager.registerCallback(listener); 242 // Wait for events generated on registration 243 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 244 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 245 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 246 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 247 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 248 249 // Inject a boolean event and wait for its callback in onPropertySet. 250 VehiclePropValue v = AidlVehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_DEFROSTER) 251 .setAreaId(VehicleAreaWindow.FRONT_WINDSHIELD) 252 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 253 .addIntValues(1) 254 .build(); 255 assertEquals(0, mAvailable.availablePermits()); 256 getAidlMockedVehicleHal().injectEvent(v); 257 258 // Verify client get the callback. 259 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 260 assertTrue(mEventBoolVal); 261 assertEquals(mEventZoneVal, VehicleAreaWindow.FRONT_WINDSHIELD); 262 263 // test unregister callback 264 mCarHvacManager.unregisterCallback(listener); 265 assertThrows(AssertionError.class, () -> getAidlMockedVehicleHal().injectEvent(v)); 266 } 267 268 private static final class HvacPropertyHandler implements VehicleHalPropertyHandler { 269 HashMap<Integer, VehiclePropValue> mMap = new HashMap<>(); 270 271 @Override onPropertySet(VehiclePropValue value)272 public synchronized void onPropertySet(VehiclePropValue value) { 273 mMap.put(value.prop, value); 274 } 275 276 @Override onPropertyGet(VehiclePropValue value)277 public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) { 278 VehiclePropValue currentValue = mMap.get(value.prop); 279 // VNS will call get method when subscribe is called, just return empty value. 280 return currentValue != null ? currentValue : value; 281 } 282 283 @Override onPropertySubscribe(int property, float sampleRate)284 public synchronized void onPropertySubscribe(int property, float sampleRate) { 285 Log.d(TAG, "onPropertySubscribe property " + property + " sampleRate " + sampleRate); 286 if (mMap.get(property) == null) { 287 Log.d(TAG, "onPropertySubscribe add placeholder property: " + property); 288 int areaId = 0; 289 switch (property) { 290 case VehicleProperty.HVAC_DEFROSTER: 291 areaId = VehicleAreaWindow.FRONT_WINDSHIELD; 292 break; 293 case VehicleProperty.HVAC_FAN_SPEED: 294 // Fall through 295 case VehicleProperty.HVAC_TEMPERATURE_SET: 296 areaId = VehicleAreaSeat.ROW_1_LEFT; 297 break; 298 case VehicleProperty.HVAC_AC_ON: 299 areaId = VehicleAreaSeat.ROW_1_CENTER; 300 break; 301 case VehicleProperty.HVAC_TEMPERATURE_CURRENT: 302 areaId = VehicleAreaSeat.ROW_1_LEFT | VehicleAreaSeat.ROW_1_RIGHT; 303 break; 304 } 305 VehiclePropValue placeholderValue = AidlVehiclePropValueBuilder.newBuilder(property) 306 .setAreaId(areaId) 307 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 308 .addIntValues(1) 309 .addFloatValues(1) 310 .build(); 311 mMap.put(property, placeholderValue); 312 } 313 } 314 315 @Override onPropertyUnsubscribe(int property)316 public synchronized void onPropertyUnsubscribe(int property) { 317 Log.d(TAG, "onPropertyUnSubscribe property " + property); 318 } 319 } 320 321 private class EventListener implements CarHvacEventCallback { EventListener()322 EventListener() { } 323 324 @Override onChangeEvent(final CarPropertyValue value)325 public void onChangeEvent(final CarPropertyValue value) { 326 Log.d(TAG, "onChangeEvent: " + value); 327 Object o = value.getValue(); 328 mEventZoneVal = value.getAreaId(); 329 330 if (o instanceof Integer) { 331 mEventIntVal = (Integer) o; 332 } else if (o instanceof Float) { 333 mEventFloatVal = (Float) o; 334 } else if (o instanceof Boolean) { 335 mEventBoolVal = (Boolean) o; 336 } 337 mAvailable.release(); 338 } 339 340 @Override onErrorEvent(final int propertyId, final int zone)341 public void onErrorEvent(final int propertyId, final int zone) { 342 Log.d(TAG, "Error: propertyId=" + propertyId + " zone=" + zone); 343 } 344 } 345 } 346