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