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.cabin.CarCabinManager; 26 import android.car.hardware.cabin.CarCabinManager.CarCabinEventCallback; 27 import android.car.hardware.cabin.CarCabinManager.PropertyId; 28 import android.hardware.automotive.vehicle.V2_0.VehicleAreaDoor; 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.os.SystemClock; 33 import android.util.Log; 34 import android.util.MutableInt; 35 36 import androidx.test.ext.junit.runners.AndroidJUnit4; 37 import androidx.test.filters.FlakyTest; 38 import androidx.test.filters.MediumTest; 39 40 import com.android.car.vehiclehal.VehiclePropValueBuilder; 41 import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler; 42 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 46 import java.util.HashMap; 47 import java.util.concurrent.CountDownLatch; 48 import java.util.concurrent.Semaphore; 49 import java.util.concurrent.TimeUnit; 50 51 @RunWith(AndroidJUnit4.class) 52 @MediumTest 53 public class CarCabinManagerTest extends MockedCarTestBase { 54 private static final String TAG = CarCabinManagerTest.class.getSimpleName(); 55 56 // Use this semaphore to block until the callback is heard of. 57 private Semaphore mAvailable; 58 59 private CarCabinManager mCarCabinManager; 60 private boolean mEventBoolVal; 61 private int mEventIntVal; 62 private int mEventZoneVal; 63 64 @Override configureMockedHal()65 protected synchronized void configureMockedHal() { 66 CabinPropertyHandler handler = new CabinPropertyHandler(); 67 addProperty(VehicleProperty.DOOR_LOCK, handler) 68 .addAreaConfig(VehicleAreaDoor.ROW_1_LEFT, 0, 0); 69 addProperty(VehicleProperty.WINDOW_POS, handler) 70 .addAreaConfig(VehicleAreaWindow.ROW_1_LEFT, 0, 0); 71 } 72 73 @Override setUp()74 public void setUp() throws Exception { 75 super.setUp(); 76 mAvailable = new Semaphore(0); 77 mCarCabinManager = (CarCabinManager) getCar().getCarManager(Car.CABIN_SERVICE); 78 } 79 80 // Test a boolean property 81 @Test testCabinDoorLockOn()82 public void testCabinDoorLockOn() throws Exception { 83 mCarCabinManager.setBooleanProperty(CarCabinManager.ID_DOOR_LOCK, 84 VehicleAreaDoor.ROW_1_LEFT, true); 85 boolean lock = mCarCabinManager.getBooleanProperty(CarCabinManager.ID_DOOR_LOCK, 86 VehicleAreaDoor.ROW_1_LEFT); 87 assertTrue(lock); 88 89 mCarCabinManager.setBooleanProperty(CarCabinManager.ID_DOOR_LOCK, 90 VehicleAreaDoor.ROW_1_LEFT, false); 91 lock = mCarCabinManager.getBooleanProperty(CarCabinManager.ID_DOOR_LOCK, 92 VehicleAreaDoor.ROW_1_LEFT); 93 assertFalse(lock); 94 } 95 96 // Test an integer property 97 @Test testCabinWindowPos()98 public void testCabinWindowPos() throws Exception { 99 mCarCabinManager.setIntProperty(CarCabinManager.ID_WINDOW_POS, 100 VehicleAreaWindow.ROW_1_LEFT, 50); 101 int windowPos = mCarCabinManager.getIntProperty(CarCabinManager.ID_WINDOW_POS, 102 VehicleAreaWindow.ROW_1_LEFT); 103 assertEquals(50, windowPos); 104 105 mCarCabinManager.setIntProperty(CarCabinManager.ID_WINDOW_POS, 106 VehicleAreaWindow.ROW_1_LEFT, 25); 107 windowPos = mCarCabinManager.getIntProperty(CarCabinManager.ID_WINDOW_POS, 108 VehicleAreaWindow.ROW_1_LEFT); 109 assertEquals(25, windowPos); 110 } 111 112 @Test testError()113 public void testError() throws Exception { 114 final int PROP = VehicleProperty.DOOR_LOCK; 115 final int AREA = VehicleAreaWindow.ROW_1_LEFT; 116 final int ERR_CODE = 42; 117 118 CountDownLatch errorLatch = new CountDownLatch(1); 119 MutableInt propertyIdReceived = new MutableInt(0); 120 MutableInt areaIdReceived = new MutableInt(0); 121 122 mCarCabinManager.registerCallback(new CarCabinEventCallback() { 123 @Override 124 public void onChangeEvent(CarPropertyValue value) { 125 126 } 127 128 @Override 129 public void onErrorEvent(@PropertyId int propertyId, int area) { 130 propertyIdReceived.value = propertyId; 131 areaIdReceived.value = area; 132 errorLatch.countDown(); 133 } 134 }); 135 mCarCabinManager.setBooleanProperty(PROP, AREA, true); 136 getMockedVehicleHal().injectError(ERR_CODE, PROP, AREA); 137 assertTrue(errorLatch.await(DEFAULT_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS)); 138 assertEquals(PROP, propertyIdReceived.value); 139 assertEquals(AREA, areaIdReceived.value); 140 } 141 142 143 // Test an event 144 @Test 145 @FlakyTest testEvent()146 public void testEvent() throws Exception { 147 mCarCabinManager.registerCallback(new EventListener()); 148 // Wait for two events generated on registration 149 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 150 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 151 // Inject a boolean event and wait for its callback in onPropertySet. 152 VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.DOOR_LOCK) 153 .setAreaId(VehicleAreaDoor.ROW_1_LEFT) 154 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 155 .addIntValue(1) 156 .build(); 157 158 assertEquals(0, mAvailable.availablePermits()); 159 getMockedVehicleHal().injectEvent(v); 160 161 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 162 assertTrue(mEventBoolVal); 163 assertEquals(VehicleAreaDoor.ROW_1_LEFT, mEventZoneVal); 164 165 // Inject an integer event and wait for its callback in onPropertySet. 166 v = VehiclePropValueBuilder.newBuilder(VehicleProperty.WINDOW_POS) 167 .setAreaId(VehicleAreaWindow.ROW_1_LEFT) 168 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 169 .addIntValue(75) 170 .build(); 171 assertEquals(0, mAvailable.availablePermits()); 172 getMockedVehicleHal().injectEvent(v); 173 174 assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS)); 175 assertEquals(mEventIntVal, 75); 176 assertEquals(VehicleAreaWindow.ROW_1_LEFT, mEventZoneVal); 177 } 178 179 180 private class CabinPropertyHandler implements VehicleHalPropertyHandler { 181 HashMap<Integer, VehiclePropValue> mMap = new HashMap<>(); 182 183 @Override onPropertySet(VehiclePropValue value)184 public synchronized void onPropertySet(VehiclePropValue value) { 185 mMap.put(value.prop, value); 186 } 187 188 @Override onPropertyGet(VehiclePropValue value)189 public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) { 190 VehiclePropValue currentValue = mMap.get(value.prop); 191 // VNS will call get method when subscribe is called, just return empty value. 192 return currentValue != null ? currentValue : value; 193 } 194 195 @Override onPropertySubscribe(int property, float sampleRate)196 public synchronized void onPropertySubscribe(int property, float sampleRate) { 197 Log.d(TAG, "onPropertySubscribe property " + property + " sampleRate " + sampleRate); 198 if (mMap.get(property) == null) { 199 Log.d(TAG, "onPropertySubscribe add placeholder property: " + property); 200 VehiclePropValue placeholderValue = VehiclePropValueBuilder.newBuilder(property) 201 .setAreaId(VehicleAreaDoor.ROW_1_LEFT) 202 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 203 .addIntValue(1) 204 .build(); 205 mMap.put(property, placeholderValue); 206 } 207 } 208 209 @Override onPropertyUnsubscribe(int property)210 public synchronized void onPropertyUnsubscribe(int property) { 211 Log.d(TAG, "onPropertyUnSubscribe property " + property); 212 } 213 } 214 215 private class EventListener implements CarCabinEventCallback { EventListener()216 EventListener() { } 217 218 @Override onChangeEvent(final CarPropertyValue value)219 public void onChangeEvent(final CarPropertyValue value) { 220 Log.d(TAG, "onChangeEvent: " + value); 221 Object o = value.getValue(); 222 mEventZoneVal = value.getAreaId(); 223 224 if (o instanceof Integer) { 225 mEventIntVal = (Integer) o; 226 } else if (o instanceof Boolean) { 227 mEventBoolVal = (Boolean) o; 228 } else { 229 Log.e(TAG, "onChangeEvent: Unknown instance type = " + o.getClass().getName()); 230 } 231 mAvailable.release(); 232 } 233 234 @Override onErrorEvent(final int propertyId, final int zone)235 public void onErrorEvent(final int propertyId, final int zone) { 236 Log.d(TAG, "Error: propertyId=" + propertyId + " zone=" + zone); 237 } 238 } 239 } 240