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