1 /* 2 * Copyright (C) 2016 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.vehiclehal.test; 18 19 import static java.lang.Integer.toHexString; 20 import static junit.framework.Assert.assertEquals; 21 import static junit.framework.Assert.assertNotNull; 22 import static junit.framework.Assert.fail; 23 24 import android.hardware.automotive.vehicle.V2_0.IVehicle; 25 import android.hardware.automotive.vehicle.V2_0.IVehicleCallback; 26 import android.hardware.automotive.vehicle.V2_0.StatusCode; 27 import android.hardware.automotive.vehicle.V2_0.SubscribeOptions; 28 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig; 29 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 30 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess; 31 import android.os.RemoteException; 32 import android.os.SystemClock; 33 34 import com.google.android.collect.Lists; 35 36 import java.util.ArrayList; 37 import java.util.HashMap; 38 import java.util.List; 39 import java.util.Map; 40 41 /** 42 * Mocked implementation of {@link IVehicle}. 43 */ 44 public class MockedVehicleHal extends IVehicle.Stub { 45 /** 46 * Interface for handler of each property. 47 */ 48 public interface VehicleHalPropertyHandler { 49 default void onPropertySet(VehiclePropValue value) {} 50 default VehiclePropValue onPropertyGet(VehiclePropValue value) { return null; } 51 default void onPropertySubscribe(int property, int zones, float sampleRate) {} 52 default void onPropertyUnsubscribe(int property) {} 53 54 VehicleHalPropertyHandler NOP = new VehicleHalPropertyHandler() {}; 55 } 56 57 private final Map<Integer, VehicleHalPropertyHandler> mPropertyHandlerMap = new HashMap<>(); 58 private final Map<Integer, VehiclePropConfig> mConfigs = new HashMap<>(); 59 private final Map<Integer, List<IVehicleCallback>> mSubscribers = new HashMap<>(); 60 61 public synchronized void addProperties(VehiclePropConfig... configs) { 62 for (VehiclePropConfig config : configs) { 63 addProperty(config, new DefaultPropertyHandler(config, null)); 64 } 65 } 66 67 public synchronized void addProperty(VehiclePropConfig config, 68 VehicleHalPropertyHandler handler) { 69 mPropertyHandlerMap.put(config.prop, handler); 70 mConfigs.put(config.prop, config); 71 } 72 73 public synchronized void addStaticProperty(VehiclePropConfig config, 74 VehiclePropValue value) { 75 addProperty(config, new StaticPropertyHandler(value)); 76 } 77 78 public boolean waitForSubscriber(int propId, long timeoutMillis) { 79 long startTime = SystemClock.elapsedRealtime(); 80 try { 81 synchronized (this) { 82 while (mSubscribers.get(propId) == null) { 83 long waitMillis = startTime - SystemClock.elapsedRealtime() + timeoutMillis; 84 if (waitMillis < 0) break; 85 wait(waitMillis); 86 } 87 88 return mSubscribers.get(propId) != null; 89 } 90 } catch (InterruptedException e) { 91 return false; 92 } 93 } 94 95 public synchronized void injectEvent(VehiclePropValue value) { 96 List<IVehicleCallback> callbacks = mSubscribers.get(value.prop); 97 assertNotNull("Injecting event failed for property: " + value.prop 98 + ". No listeners found", callbacks); 99 for (IVehicleCallback callback : callbacks) { 100 try { 101 callback.onPropertyEvent(Lists.newArrayList(value)); 102 } catch (RemoteException e) { 103 e.printStackTrace(); 104 fail("Remote exception while injecting events."); 105 } 106 } 107 } 108 109 public synchronized void injectError(int errorCode, int propertyId, int areaId) { 110 List<IVehicleCallback> callbacks = mSubscribers.get(propertyId); 111 assertNotNull("Injecting error failed for property: " + propertyId 112 + ". No listeners found", callbacks); 113 for (IVehicleCallback callback : callbacks) { 114 try { 115 callback.onPropertySetError(errorCode, propertyId, areaId); 116 } catch (RemoteException e) { 117 e.printStackTrace(); 118 fail("Remote exception while injecting errors."); 119 } 120 } 121 } 122 123 @Override 124 public synchronized ArrayList<VehiclePropConfig> getAllPropConfigs() { 125 return new ArrayList<>(mConfigs.values()); 126 } 127 128 @Override 129 public synchronized void getPropConfigs(ArrayList<Integer> props, getPropConfigsCallback cb) { 130 ArrayList<VehiclePropConfig> res = new ArrayList<>(); 131 for (Integer prop : props) { 132 VehiclePropConfig config = mConfigs.get(prop); 133 if (config == null) { 134 cb.onValues(StatusCode.INVALID_ARG, new ArrayList<>()); 135 return; 136 } 137 res.add(config); 138 } 139 cb.onValues(StatusCode.OK, res); 140 } 141 142 @Override 143 public synchronized void get(VehiclePropValue requestedPropValue, getCallback cb) { 144 VehicleHalPropertyHandler handler = mPropertyHandlerMap.get(requestedPropValue.prop); 145 if (handler == null) { 146 cb.onValues(StatusCode.INVALID_ARG, null); 147 } else { 148 cb.onValues(StatusCode.OK, handler.onPropertyGet(requestedPropValue)); 149 } 150 } 151 152 @Override 153 public synchronized int set(VehiclePropValue propValue) { 154 VehicleHalPropertyHandler handler = mPropertyHandlerMap.get(propValue.prop); 155 if (handler == null) { 156 return StatusCode.INVALID_ARG; 157 } else { 158 handler.onPropertySet(propValue); 159 return StatusCode.OK; 160 } 161 } 162 163 @Override 164 public synchronized int subscribe(IVehicleCallback callback, 165 ArrayList<SubscribeOptions> options) { 166 for (SubscribeOptions opt : options) { 167 VehicleHalPropertyHandler handler = mPropertyHandlerMap.get(opt.propId); 168 if (handler == null) { 169 return StatusCode.INVALID_ARG; 170 } 171 172 handler.onPropertySubscribe(opt.propId, opt.vehicleAreas, opt.sampleRate); 173 List<IVehicleCallback> subscribers = mSubscribers.get(opt.propId); 174 if (subscribers == null) { 175 subscribers = new ArrayList<>(); 176 mSubscribers.put(opt.propId, subscribers); 177 notifyAll(); 178 } 179 subscribers.add(callback); 180 } 181 return StatusCode.OK; 182 } 183 184 @Override 185 public synchronized int unsubscribe(IVehicleCallback callback, int propId) { 186 VehicleHalPropertyHandler handler = mPropertyHandlerMap.get(propId); 187 if (handler == null) { 188 return StatusCode.INVALID_ARG; 189 } 190 191 handler.onPropertyUnsubscribe(propId); 192 List<IVehicleCallback> subscribers = mSubscribers.get(propId); 193 if (subscribers != null) { 194 subscribers.remove(callback); 195 if (subscribers.size() == 0) { 196 mSubscribers.remove(propId); 197 } 198 } 199 return StatusCode.OK; 200 } 201 202 @Override 203 public String debugDump() { 204 return null; 205 } 206 207 public static class FailingPropertyHandler implements VehicleHalPropertyHandler { 208 @Override 209 public void onPropertySet(VehiclePropValue value) { 210 fail("Unexpected onPropertySet call"); 211 } 212 213 @Override 214 public VehiclePropValue onPropertyGet(VehiclePropValue value) { 215 fail("Unexpected onPropertyGet call"); 216 return null; 217 } 218 219 @Override 220 public void onPropertySubscribe(int property, int zones, float sampleRate) { 221 fail("Unexpected onPropertySubscribe call"); 222 } 223 224 @Override 225 public void onPropertyUnsubscribe(int property) { 226 fail("Unexpected onPropertyUnsubscribe call"); 227 } 228 } 229 230 public static class StaticPropertyHandler extends FailingPropertyHandler { 231 private final VehiclePropValue mValue; 232 233 public StaticPropertyHandler(VehiclePropValue value) { 234 mValue = value; 235 } 236 237 @Override 238 public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) { 239 return mValue; 240 } 241 } 242 243 public static class DefaultPropertyHandler implements VehicleHalPropertyHandler { 244 private final VehiclePropConfig mConfig; 245 private VehiclePropValue mValue; 246 private boolean mSubscribed = false; 247 248 public DefaultPropertyHandler(VehiclePropConfig config, VehiclePropValue initialValue) { 249 mConfig = config; 250 mValue = initialValue; 251 } 252 253 @Override 254 public synchronized void onPropertySet(VehiclePropValue value) { 255 assertEquals(mConfig.prop, value.prop); 256 assertEquals(VehiclePropertyAccess.WRITE, mConfig.access & VehiclePropertyAccess.WRITE); 257 mValue = value; 258 } 259 260 @Override 261 public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) { 262 assertEquals(mConfig.prop, value.prop); 263 assertEquals(VehiclePropertyAccess.READ, mConfig.access & VehiclePropertyAccess.READ); 264 return mValue; 265 } 266 267 @Override 268 public synchronized void onPropertySubscribe(int property, int zones, float sampleRate) { 269 assertEquals(mConfig.prop, property); 270 mSubscribed = true; 271 } 272 273 @Override 274 public synchronized void onPropertyUnsubscribe(int property) { 275 assertEquals(mConfig.prop, property); 276 if (!mSubscribed) { 277 throw new IllegalArgumentException("Property was not subscribed 0x" 278 + toHexString( property)); 279 } 280 mSubscribed = false; 281 } 282 } 283 } 284