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.V2_0.VehicleAreaSeat; 32 import android.hardware.automotive.vehicle.V2_0.VehicleAreaWindow; 33 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 34 import android.hardware.automotive.vehicle.V2_0.VehicleProperty; 35 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess; 36 import android.hardware.automotive.vehicle.V2_0.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.vehiclehal.VehiclePropValueBuilder; 45 import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler; 46 47 import junit.framework.AssertionFailedError; 48 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 52 import java.util.HashMap; 53 import java.util.concurrent.CountDownLatch; 54 import java.util.concurrent.Semaphore; 55 import java.util.concurrent.TimeUnit; 56 57 @RunWith(AndroidJUnit4.class) 58 @MediumTest 59 public class CarHvacManagerTest extends MockedCarTestBase { 60 private static final String TAG = CarHvacManagerTest.class.getSimpleName(); 61 62 // Use this semaphore to block until the callback is heard of. 63 private Semaphore mAvailable; 64 65 private CarHvacManager mCarHvacManager; 66 private boolean mEventBoolVal; 67 private float mEventFloatVal; 68 private int mEventIntVal; 69 private int mEventZoneVal; 70 71 @Override configureMockedHal()72 protected synchronized void configureMockedHal() { 73 HvacPropertyHandler handler = new HvacPropertyHandler(); 74 addProperty(VehicleProperty.HVAC_DEFROSTER, handler) 75 .addAreaConfig(VehicleAreaWindow.FRONT_WINDSHIELD, 0, 0); 76 addProperty(VehicleProperty.HVAC_FAN_SPEED, handler) 77 .addAreaConfig(VehicleAreaSeat.ROW_1_LEFT, 0, 0); 78 addProperty(VehicleProperty.HVAC_TEMPERATURE_SET, handler) 79 .addAreaConfig(VehicleAreaSeat.ROW_1_LEFT, 0, 0); 80 addProperty(VehicleProperty.HVAC_TEMPERATURE_CURRENT, handler) 81 .setChangeMode(VehiclePropertyChangeMode.CONTINUOUS) 82 .setAccess(VehiclePropertyAccess.READ) 83 .addAreaConfig(VehicleAreaSeat.ROW_1_LEFT | VehicleAreaSeat.ROW_1_RIGHT, 0, 0); 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 getMockedVehicleHal().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 194 // Inject a boolean event and wait for its callback in onPropertySet. 195 VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_DEFROSTER) 196 .setAreaId(VehicleAreaWindow.FRONT_WINDSHIELD) 197 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 198 .addIntValue(1) 199 .build(); 200 assertEquals(0, mAvailable.availablePermits()); 201 getMockedVehicleHal().injectEvent(v); 202 203 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 204 assertTrue(mEventBoolVal); 205 assertEquals(mEventZoneVal, VehicleAreaWindow.FRONT_WINDSHIELD); 206 207 // Inject a float event and wait for its callback in onPropertySet. 208 v = VehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_TEMPERATURE_CURRENT) 209 .setAreaId(VehicleAreaSeat.ROW_1_LEFT | VehicleAreaSeat.ROW_1_RIGHT) 210 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 211 .addFloatValue(67f) 212 .build(); 213 assertEquals(0, mAvailable.availablePermits()); 214 getMockedVehicleHal().injectEvent(v); 215 216 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 217 assertEquals(67, mEventFloatVal, 0); 218 assertEquals(VehicleAreaSeat.ROW_1_LEFT | VehicleAreaSeat.ROW_1_RIGHT, mEventZoneVal); 219 220 // Inject an integer event and wait for its callback in onPropertySet. 221 v = VehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_FAN_SPEED) 222 .setAreaId(VehicleAreaSeat.ROW_1_LEFT) 223 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 224 .addIntValue(4) 225 .build(); 226 assertEquals(0, mAvailable.availablePermits()); 227 getMockedVehicleHal().injectEvent(v); 228 229 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 230 assertEquals(4, mEventIntVal); 231 assertEquals(VehicleAreaSeat.ROW_1_LEFT, mEventZoneVal); 232 } 233 234 /** 235 * Test {@link CarHvacManager#unregisterCallback(CarHvacEventCallback)} 236 */ 237 @Test testUnregisterCallback()238 public void testUnregisterCallback() throws Exception { 239 EventListener listener = new EventListener(); 240 mCarHvacManager.registerCallback(listener); 241 // Wait for events generated on registration 242 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 243 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 244 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 245 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 246 247 // Inject a boolean event and wait for its callback in onPropertySet. 248 VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_DEFROSTER) 249 .setAreaId(VehicleAreaWindow.FRONT_WINDSHIELD) 250 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 251 .addIntValue(1) 252 .build(); 253 assertEquals(0, mAvailable.availablePermits()); 254 getMockedVehicleHal().injectEvent(v); 255 256 // Verify client get the callback. 257 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 258 assertTrue(mEventBoolVal); 259 assertEquals(mEventZoneVal, VehicleAreaWindow.FRONT_WINDSHIELD); 260 261 // test unregister callback 262 mCarHvacManager.unregisterCallback(listener); 263 assertThrows(AssertionFailedError.class, () -> getMockedVehicleHal().injectEvent(v)); 264 } 265 266 private class HvacPropertyHandler implements VehicleHalPropertyHandler { 267 HashMap<Integer, VehiclePropValue> mMap = new HashMap<>(); 268 269 @Override onPropertySet(VehiclePropValue value)270 public synchronized void onPropertySet(VehiclePropValue value) { 271 mMap.put(value.prop, value); 272 } 273 274 @Override onPropertyGet(VehiclePropValue value)275 public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) { 276 VehiclePropValue currentValue = mMap.get(value.prop); 277 // VNS will call get method when subscribe is called, just return empty value. 278 return currentValue != null ? currentValue : value; 279 } 280 281 @Override onPropertySubscribe(int property, float sampleRate)282 public synchronized void onPropertySubscribe(int property, float sampleRate) { 283 Log.d(TAG, "onPropertySubscribe property " + property + " sampleRate " + sampleRate); 284 if (mMap.get(property) == null) { 285 Log.d(TAG, "onPropertySubscribe add placeholder property: " + property); 286 VehiclePropValue placeholderValue = VehiclePropValueBuilder.newBuilder(property) 287 .setAreaId(0) 288 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 289 .addIntValue(1) 290 .addFloatValue(1) 291 .build(); 292 mMap.put(property, placeholderValue); 293 } 294 } 295 296 @Override onPropertyUnsubscribe(int property)297 public synchronized void onPropertyUnsubscribe(int property) { 298 Log.d(TAG, "onPropertyUnSubscribe property " + property); 299 } 300 } 301 302 private class EventListener implements CarHvacEventCallback { EventListener()303 EventListener() { } 304 305 @Override onChangeEvent(final CarPropertyValue value)306 public void onChangeEvent(final CarPropertyValue value) { 307 Log.d(TAG, "onChangeEvent: " + value); 308 Object o = value.getValue(); 309 mEventZoneVal = value.getAreaId(); 310 311 if (o instanceof Integer) { 312 mEventIntVal = (Integer) o; 313 } else if (o instanceof Float) { 314 mEventFloatVal = (Float) o; 315 } else if (o instanceof Boolean) { 316 mEventBoolVal = (Boolean) o; 317 } 318 mAvailable.release(); 319 } 320 321 @Override onErrorEvent(final int propertyId, final int zone)322 public void onErrorEvent(final int propertyId, final int zone) { 323 Log.d(TAG, "Error: propertyId=" + propertyId + " zone=" + zone); 324 } 325 } 326 } 327