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 org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertTrue; 22 23 import android.car.Car; 24 import android.car.hardware.CarPropertyValue; 25 import android.car.hardware.hvac.CarHvacManager; 26 import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback; 27 import android.car.hardware.hvac.CarHvacManager.PropertyId; 28 import android.hardware.automotive.vehicle.V2_0.VehicleAreaSeat; 29 import android.hardware.automotive.vehicle.V2_0.VehicleAreaWindow; 30 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 31 import android.hardware.automotive.vehicle.V2_0.VehicleProperty; 32 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess; 33 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode; 34 import android.os.SystemClock; 35 import android.util.Log; 36 import android.util.MutableInt; 37 38 import androidx.test.filters.MediumTest; 39 import androidx.test.runner.AndroidJUnit4; 40 41 import com.android.car.vehiclehal.VehiclePropValueBuilder; 42 import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler; 43 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 47 import java.util.HashMap; 48 import java.util.concurrent.CountDownLatch; 49 import java.util.concurrent.Semaphore; 50 import java.util.concurrent.TimeUnit; 51 52 @RunWith(AndroidJUnit4.class) 53 @MediumTest 54 public class CarHvacManagerTest extends MockedCarTestBase { 55 private static final String TAG = CarHvacManagerTest.class.getSimpleName(); 56 57 // Use this semaphore to block until the callback is heard of. 58 private Semaphore mAvailable; 59 60 private CarHvacManager mCarHvacManager; 61 private boolean mEventBoolVal; 62 private float mEventFloatVal; 63 private int mEventIntVal; 64 private int mEventZoneVal; 65 66 @Override configureMockedHal()67 protected synchronized void configureMockedHal() { 68 HvacPropertyHandler handler = new HvacPropertyHandler(); 69 addProperty(VehicleProperty.HVAC_DEFROSTER, handler) 70 .addAreaConfig(VehicleAreaWindow.FRONT_WINDSHIELD, 0, 0); 71 addProperty(VehicleProperty.HVAC_FAN_SPEED, handler) 72 .addAreaConfig(VehicleAreaSeat.ROW_1_LEFT, 0, 0); 73 addProperty(VehicleProperty.HVAC_TEMPERATURE_SET, handler) 74 .addAreaConfig(VehicleAreaSeat.ROW_1_LEFT, 0, 0); 75 addProperty(VehicleProperty.HVAC_TEMPERATURE_CURRENT, handler) 76 .setChangeMode(VehiclePropertyChangeMode.CONTINUOUS) 77 .setAccess(VehiclePropertyAccess.READ) 78 .addAreaConfig(VehicleAreaSeat.ROW_1_LEFT | VehicleAreaSeat.ROW_1_RIGHT, 0, 0); 79 } 80 81 @Override setUp()82 public void setUp() throws Exception { 83 super.setUp(); 84 mAvailable = new Semaphore(0); 85 mCarHvacManager = (CarHvacManager) getCar().getCarManager(Car.HVAC_SERVICE); 86 } 87 88 // Test a boolean property 89 @Test testHvacRearDefrosterOn()90 public void testHvacRearDefrosterOn() throws Exception { 91 mCarHvacManager.setBooleanProperty(CarHvacManager.ID_WINDOW_DEFROSTER_ON, 92 VehicleAreaWindow.FRONT_WINDSHIELD, true); 93 boolean defrost = mCarHvacManager.getBooleanProperty(CarHvacManager.ID_WINDOW_DEFROSTER_ON, 94 VehicleAreaWindow.FRONT_WINDSHIELD); 95 assertTrue(defrost); 96 97 mCarHvacManager.setBooleanProperty(CarHvacManager.ID_WINDOW_DEFROSTER_ON, 98 VehicleAreaWindow.FRONT_WINDSHIELD, false); 99 defrost = mCarHvacManager.getBooleanProperty(CarHvacManager.ID_WINDOW_DEFROSTER_ON, 100 VehicleAreaWindow.FRONT_WINDSHIELD); 101 assertFalse(defrost); 102 } 103 104 // Test an integer property 105 @Test testHvacFanSpeed()106 public void testHvacFanSpeed() throws Exception { 107 mCarHvacManager.setIntProperty(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, 108 VehicleAreaSeat.ROW_1_LEFT, 15); 109 int speed = mCarHvacManager.getIntProperty(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, 110 VehicleAreaSeat.ROW_1_LEFT); 111 assertEquals(15, speed); 112 113 mCarHvacManager.setIntProperty(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, 114 VehicleAreaSeat.ROW_1_LEFT, 23); 115 speed = mCarHvacManager.getIntProperty(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, 116 VehicleAreaSeat.ROW_1_LEFT); 117 assertEquals(23, speed); 118 } 119 120 // Test an float property 121 @Test testHvacTempSetpoint()122 public void testHvacTempSetpoint() throws Exception { 123 mCarHvacManager.setFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT, 124 VehicleAreaSeat.ROW_1_LEFT, 70); 125 float temp = mCarHvacManager.getFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT, 126 VehicleAreaSeat.ROW_1_LEFT); 127 assertEquals(70.0, temp, 0); 128 129 mCarHvacManager.setFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT, 130 VehicleAreaSeat.ROW_1_LEFT, (float) 65.5); 131 temp = mCarHvacManager.getFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT, 132 VehicleAreaSeat.ROW_1_LEFT); 133 assertEquals(65.5, temp, 0); 134 } 135 136 @Test testError()137 public void testError() throws Exception { 138 final int PROP = VehicleProperty.HVAC_DEFROSTER; 139 final int AREA = VehicleAreaWindow.FRONT_WINDSHIELD; 140 final int ERR_CODE = 42; 141 142 CountDownLatch errorLatch = new CountDownLatch(1); 143 MutableInt propertyIdReceived = new MutableInt(0); 144 MutableInt areaIdReceived = new MutableInt(0); 145 146 mCarHvacManager.registerCallback(new CarHvacEventCallback() { 147 @Override 148 public void onChangeEvent(CarPropertyValue value) { 149 150 } 151 152 @Override 153 public void onErrorEvent(@PropertyId int propertyId, int area) { 154 propertyIdReceived.value = propertyId; 155 areaIdReceived.value = area; 156 errorLatch.countDown(); 157 } 158 }); 159 160 getMockedVehicleHal().injectError(ERR_CODE, PROP, AREA); 161 assertTrue(errorLatch.await(DEFAULT_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS)); 162 assertEquals(PROP, propertyIdReceived.value); 163 assertEquals(AREA, areaIdReceived.value); 164 } 165 166 // Test an event 167 @Test testEvent()168 public void testEvent() throws Exception { 169 mCarHvacManager.registerCallback(new EventListener()); 170 // Wait for events generated on registration 171 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 172 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 173 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 174 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 175 176 // Inject a boolean event and wait for its callback in onPropertySet. 177 VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_DEFROSTER) 178 .setAreaId(VehicleAreaWindow.FRONT_WINDSHIELD) 179 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 180 .addIntValue(1) 181 .build(); 182 assertEquals(0, mAvailable.availablePermits()); 183 getMockedVehicleHal().injectEvent(v); 184 185 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 186 assertTrue(mEventBoolVal); 187 assertEquals(mEventZoneVal, VehicleAreaWindow.FRONT_WINDSHIELD); 188 189 // Inject a float event and wait for its callback in onPropertySet. 190 v = VehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_TEMPERATURE_CURRENT) 191 .setAreaId(VehicleAreaSeat.ROW_1_LEFT | VehicleAreaSeat.ROW_1_RIGHT) 192 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 193 .addFloatValue(67f) 194 .build(); 195 assertEquals(0, mAvailable.availablePermits()); 196 getMockedVehicleHal().injectEvent(v); 197 198 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 199 assertEquals(67, mEventFloatVal, 0); 200 assertEquals(VehicleAreaSeat.ROW_1_LEFT | VehicleAreaSeat.ROW_1_RIGHT, mEventZoneVal); 201 202 // Inject an integer event and wait for its callback in onPropertySet. 203 v = VehiclePropValueBuilder.newBuilder(VehicleProperty.HVAC_FAN_SPEED) 204 .setAreaId(VehicleAreaSeat.ROW_1_LEFT) 205 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 206 .addIntValue(4) 207 .build(); 208 assertEquals(0, mAvailable.availablePermits()); 209 getMockedVehicleHal().injectEvent(v); 210 211 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 212 assertEquals(4, mEventIntVal); 213 assertEquals(VehicleAreaSeat.ROW_1_LEFT, mEventZoneVal); 214 } 215 216 private class HvacPropertyHandler implements VehicleHalPropertyHandler { 217 HashMap<Integer, VehiclePropValue> mMap = new HashMap<>(); 218 219 @Override onPropertySet(VehiclePropValue value)220 public synchronized void onPropertySet(VehiclePropValue value) { 221 mMap.put(value.prop, value); 222 } 223 224 @Override onPropertyGet(VehiclePropValue value)225 public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) { 226 VehiclePropValue currentValue = mMap.get(value.prop); 227 // VNS will call get method when subscribe is called, just return empty value. 228 return currentValue != null ? currentValue : value; 229 } 230 231 @Override onPropertySubscribe(int property, float sampleRate)232 public synchronized void onPropertySubscribe(int property, float sampleRate) { 233 Log.d(TAG, "onPropertySubscribe property " + property + " sampleRate " + sampleRate); 234 if (mMap.get(property) == null) { 235 Log.d(TAG, "onPropertySubscribe add dummy property: " + property); 236 VehiclePropValue dummyValue = VehiclePropValueBuilder.newBuilder(property) 237 .setAreaId(0) 238 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 239 .addIntValue(1) 240 .addFloatValue(1) 241 .build(); 242 mMap.put(property, dummyValue); 243 } 244 } 245 246 @Override onPropertyUnsubscribe(int property)247 public synchronized void onPropertyUnsubscribe(int property) { 248 Log.d(TAG, "onPropertyUnSubscribe property " + property); 249 } 250 } 251 252 private class EventListener implements CarHvacEventCallback { EventListener()253 EventListener() { } 254 255 @Override onChangeEvent(final CarPropertyValue value)256 public void onChangeEvent(final CarPropertyValue value) { 257 Log.d(TAG, "onChangeEvent: " + value); 258 Object o = value.getValue(); 259 mEventZoneVal = value.getAreaId(); 260 261 if (o instanceof Integer) { 262 mEventIntVal = (Integer) o; 263 } else if (o instanceof Float) { 264 mEventFloatVal = (Float) o; 265 } else if (o instanceof Boolean) { 266 mEventBoolVal = (Boolean) o; 267 } 268 mAvailable.release(); 269 } 270 271 @Override onErrorEvent(final int propertyId, final int zone)272 public void onErrorEvent(final int propertyId, final int zone) { 273 Log.d(TAG, "Error: propertyId=" + propertyId + " zone=" + zone); 274 } 275 } 276 } 277