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.assertTrue; 24 import static org.testng.Assert.assertThrows; 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 69 @Override configureMockedHal()70 protected void configureMockedHal() { 71 HvacPropertyHandler handler = new HvacPropertyHandler(); 72 addAidlProperty(VehicleProperty.HVAC_DEFROSTER, handler) 73 .addAreaConfig(VehicleAreaWindow.FRONT_WINDSHIELD, 0, 0); 74 addAidlProperty(VehicleProperty.HVAC_FAN_SPEED, handler) 75 .addAreaConfig(VehicleAreaSeat.ROW_1_LEFT, 0, 0); 76 addAidlProperty(VehicleProperty.HVAC_TEMPERATURE_SET, handler) 77 .addAreaConfig(VehicleAreaSeat.ROW_1_LEFT, 0, 0); 78 addAidlProperty(VehicleProperty.HVAC_TEMPERATURE_CURRENT, handler) 79 .setChangeMode(VehiclePropertyChangeMode.CONTINUOUS) 80 .setAccess(VehiclePropertyAccess.READ) 81 .addAreaConfig(VehicleAreaSeat.ROW_1_LEFT | VehicleAreaSeat.ROW_1_RIGHT, 0, 0); 82 } 83 84 @Override setUp()85 public void setUp() throws Exception { 86 super.setUp(); 87 mAvailable = new Semaphore(0); 88 mCarHvacManager = (CarHvacManager) getCar().getCarManager(Car.HVAC_SERVICE); 89 mCarHvacManager.setIntProperty(VehicleProperty.HVAC_FAN_SPEED, 90 VehicleAreaSeat.ROW_1_LEFT, 0); 91 } 92 93 // Test a boolean property 94 @Test testHvacRearDefrosterOn()95 public void testHvacRearDefrosterOn() throws Exception { 96 mCarHvacManager.setBooleanProperty(CarHvacManager.ID_WINDOW_DEFROSTER_ON, 97 VehicleAreaWindow.FRONT_WINDSHIELD, true); 98 boolean defrost = mCarHvacManager.getBooleanProperty(CarHvacManager.ID_WINDOW_DEFROSTER_ON, 99 VehicleAreaWindow.FRONT_WINDSHIELD); 100 assertTrue(defrost); 101 102 mCarHvacManager.setBooleanProperty(CarHvacManager.ID_WINDOW_DEFROSTER_ON, 103 VehicleAreaWindow.FRONT_WINDSHIELD, false); 104 defrost = mCarHvacManager.getBooleanProperty(CarHvacManager.ID_WINDOW_DEFROSTER_ON, 105 VehicleAreaWindow.FRONT_WINDSHIELD); 106 assertFalse(defrost); 107 } 108 109 /** 110 * Test {@link CarHvacManager#isPropertyAvailable(int, int)} 111 */ 112 @Test testHvacPropertyAvailable()113 public void testHvacPropertyAvailable() { 114 assertThat(mCarHvacManager.isPropertyAvailable(VehicleProperty.HVAC_AC_ON, 115 VehicleAreaSeat.ROW_1_CENTER)).isFalse(); 116 assertThat(mCarHvacManager.isPropertyAvailable(VehicleProperty.HVAC_FAN_SPEED, 117 VehicleAreaSeat.ROW_1_LEFT)).isTrue(); 118 } 119 120 // Test an integer property 121 @Test testHvacFanSpeed()122 public void testHvacFanSpeed() throws Exception { 123 mCarHvacManager.setIntProperty(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, 124 VehicleAreaSeat.ROW_1_LEFT, 15); 125 int speed = mCarHvacManager.getIntProperty(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, 126 VehicleAreaSeat.ROW_1_LEFT); 127 assertEquals(15, speed); 128 129 mCarHvacManager.setIntProperty(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, 130 VehicleAreaSeat.ROW_1_LEFT, 23); 131 speed = mCarHvacManager.getIntProperty(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, 132 VehicleAreaSeat.ROW_1_LEFT); 133 assertEquals(23, speed); 134 } 135 136 // Test an float property 137 @Test testHvacTempSetpoint()138 public void testHvacTempSetpoint() throws Exception { 139 mCarHvacManager.setFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT, 140 VehicleAreaSeat.ROW_1_LEFT, 70); 141 float temp = mCarHvacManager.getFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT, 142 VehicleAreaSeat.ROW_1_LEFT); 143 assertEquals(70.0, temp, 0); 144 145 mCarHvacManager.setFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT, 146 VehicleAreaSeat.ROW_1_LEFT, (float) 65.5); 147 temp = mCarHvacManager.getFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT, 148 VehicleAreaSeat.ROW_1_LEFT); 149 assertEquals(65.5, temp, 0); 150 } 151 152 @Test testError()153 public void testError() throws Exception { 154 final int PROP = VehicleProperty.HVAC_DEFROSTER; 155 final int AREA = VehicleAreaWindow.FRONT_WINDSHIELD; 156 final int ERR_CODE = 42; 157 158 CountDownLatch errorLatch = new CountDownLatch(1); 159 MutableInt propertyIdReceived = new MutableInt(0); 160 MutableInt areaIdReceived = new MutableInt(0); 161 162 mCarHvacManager.registerCallback(new CarHvacEventCallback() { 163 @Override 164 public void onChangeEvent(CarPropertyValue value) { 165 166 } 167 168 @Override 169 public void onErrorEvent(@PropertyId int propertyId, int area) { 170 propertyIdReceived.value = propertyId; 171 areaIdReceived.value = area; 172 errorLatch.countDown(); 173 } 174 }); 175 mCarHvacManager.setBooleanProperty(PROP, AREA, true); 176 getAidlMockedVehicleHal().injectError(ERR_CODE, PROP, AREA); 177 assertTrue(errorLatch.await(DEFAULT_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS)); 178 assertEquals(PROP, propertyIdReceived.value); 179 assertEquals(AREA, areaIdReceived.value); 180 } 181 182 // Test an event 183 @Test testEvent()184 public void testEvent() throws Exception { 185 mCarHvacManager.registerCallback(new EventListener()); 186 // Wait for events generated on registration 187 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 188 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 189 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 190 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 191 192 // Inject a boolean event and wait for its callback in onPropertySet. 193 VehiclePropValue v = AidlVehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_DEFROSTER) 194 .setAreaId(VehicleAreaWindow.FRONT_WINDSHIELD) 195 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 196 .addIntValues(1) 197 .build(); 198 assertEquals(0, mAvailable.availablePermits()); 199 getAidlMockedVehicleHal().injectEvent(v); 200 201 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 202 assertTrue(mEventBoolVal); 203 assertEquals(mEventZoneVal, VehicleAreaWindow.FRONT_WINDSHIELD); 204 205 // Inject a float event and wait for its callback in onPropertySet. 206 v = AidlVehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_TEMPERATURE_CURRENT) 207 .setAreaId(VehicleAreaSeat.ROW_1_LEFT | VehicleAreaSeat.ROW_1_RIGHT) 208 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 209 .addFloatValues(67f) 210 .build(); 211 assertEquals(0, mAvailable.availablePermits()); 212 getAidlMockedVehicleHal().injectEvent(v); 213 214 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 215 assertEquals(67, mEventFloatVal, 0); 216 assertEquals(VehicleAreaSeat.ROW_1_LEFT | VehicleAreaSeat.ROW_1_RIGHT, mEventZoneVal); 217 218 // Inject an integer event and wait for its callback in onPropertySet. 219 v = AidlVehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_FAN_SPEED) 220 .setAreaId(VehicleAreaSeat.ROW_1_LEFT) 221 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 222 .addIntValues(4) 223 .build(); 224 assertEquals(0, mAvailable.availablePermits()); 225 getAidlMockedVehicleHal().injectEvent(v); 226 227 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 228 assertEquals(4, mEventIntVal); 229 assertEquals(VehicleAreaSeat.ROW_1_LEFT, mEventZoneVal); 230 } 231 232 /** 233 * Test {@link CarHvacManager#unregisterCallback(CarHvacEventCallback)} 234 */ 235 @Test testUnregisterCallback()236 public void testUnregisterCallback() throws Exception { 237 EventListener listener = new EventListener(); 238 mCarHvacManager.registerCallback(listener); 239 // Wait for events generated on registration 240 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 241 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 242 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 243 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 244 245 // Inject a boolean event and wait for its callback in onPropertySet. 246 VehiclePropValue v = AidlVehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_DEFROSTER) 247 .setAreaId(VehicleAreaWindow.FRONT_WINDSHIELD) 248 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 249 .addIntValues(1) 250 .build(); 251 assertEquals(0, mAvailable.availablePermits()); 252 getAidlMockedVehicleHal().injectEvent(v); 253 254 // Verify client get the callback. 255 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 256 assertTrue(mEventBoolVal); 257 assertEquals(mEventZoneVal, VehicleAreaWindow.FRONT_WINDSHIELD); 258 259 // test unregister callback 260 mCarHvacManager.unregisterCallback(listener); 261 assertThrows(AssertionError.class, () -> getAidlMockedVehicleHal().injectEvent(v)); 262 } 263 264 private class HvacPropertyHandler implements VehicleHalPropertyHandler { 265 HashMap<Integer, VehiclePropValue> mMap = new HashMap<>(); 266 267 @Override onPropertySet(VehiclePropValue value)268 public synchronized void onPropertySet(VehiclePropValue value) { 269 mMap.put(value.prop, value); 270 } 271 272 @Override onPropertyGet(VehiclePropValue value)273 public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) { 274 VehiclePropValue currentValue = mMap.get(value.prop); 275 // VNS will call get method when subscribe is called, just return empty value. 276 return currentValue != null ? currentValue : value; 277 } 278 279 @Override onPropertySubscribe(int property, float sampleRate)280 public synchronized void onPropertySubscribe(int property, float sampleRate) { 281 Log.d(TAG, "onPropertySubscribe property " + property + " sampleRate " + sampleRate); 282 if (mMap.get(property) == null) { 283 Log.d(TAG, "onPropertySubscribe add placeholder property: " + property); 284 VehiclePropValue placeholderValue = AidlVehiclePropValueBuilder.newBuilder(property) 285 .setAreaId(0) 286 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 287 .addIntValues(1) 288 .addFloatValues(1) 289 .build(); 290 mMap.put(property, placeholderValue); 291 } 292 } 293 294 @Override onPropertyUnsubscribe(int property)295 public synchronized void onPropertyUnsubscribe(int property) { 296 Log.d(TAG, "onPropertyUnSubscribe property " + property); 297 } 298 } 299 300 private class EventListener implements CarHvacEventCallback { EventListener()301 EventListener() { } 302 303 @Override onChangeEvent(final CarPropertyValue value)304 public void onChangeEvent(final CarPropertyValue value) { 305 Log.d(TAG, "onChangeEvent: " + value); 306 Object o = value.getValue(); 307 mEventZoneVal = value.getAreaId(); 308 309 if (o instanceof Integer) { 310 mEventIntVal = (Integer) o; 311 } else if (o instanceof Float) { 312 mEventFloatVal = (Float) o; 313 } else if (o instanceof Boolean) { 314 mEventBoolVal = (Boolean) o; 315 } 316 mAvailable.release(); 317 } 318 319 @Override onErrorEvent(final int propertyId, final int zone)320 public void onErrorEvent(final int propertyId, final int zone) { 321 Log.d(TAG, "Error: propertyId=" + propertyId + " zone=" + zone); 322 } 323 } 324 } 325