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