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.hal.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.ServiceSpecificException; 34 import android.os.SystemClock; 35 import android.util.Log; 36 37 import com.android.internal.annotations.GuardedBy; 38 39 import java.util.ArrayList; 40 import java.util.HashMap; 41 import java.util.List; 42 import java.util.Map; 43 44 import javax.annotation.concurrent.NotThreadSafe; 45 46 /** 47 * Mocked implementation of {@link IVehicle}. 48 */ 49 public class HidlMockedVehicleHal extends IVehicle.Stub { 50 private static final String TAG = HidlMockedVehicleHal.class.getSimpleName(); 51 52 private final Object mLock = new Object(); 53 54 /** 55 * Interface for handler of each property. 56 */ 57 public interface VehicleHalPropertyHandler { onPropertySet(VehiclePropValue value)58 default void onPropertySet(VehiclePropValue value) {} 59 onPropertyGet(VehiclePropValue value)60 default VehiclePropValue onPropertyGet(VehiclePropValue value) { 61 return null; 62 } 63 onPropertySubscribe(int property, float sampleRate)64 default void onPropertySubscribe(int property, float sampleRate) {} 65 onPropertyUnsubscribe(int property)66 default void onPropertyUnsubscribe(int property) {} 67 68 VehicleHalPropertyHandler NOP = new VehicleHalPropertyHandler() {}; 69 } 70 71 @GuardedBy("mLock") 72 private final Map<Integer, VehicleHalPropertyHandler> mPropertyHandlerMap = new HashMap<>(); 73 74 @GuardedBy("mLock") 75 private final Map<Integer, VehiclePropConfig> mConfigs = new HashMap<>(); 76 77 @GuardedBy("mLock") 78 private final Map<Integer, List<IVehicleCallback>> mSubscribers = new HashMap<>(); 79 addProperties(VehiclePropConfig... configs)80 public void addProperties(VehiclePropConfig... configs) { 81 synchronized (mLock) { 82 for (VehiclePropConfig config : configs) { 83 addProperty(config, new DefaultPropertyHandler(config, null)); 84 } 85 } 86 } 87 addProperty(VehiclePropConfig config, VehicleHalPropertyHandler handler)88 public void addProperty(VehiclePropConfig config, VehicleHalPropertyHandler handler) { 89 synchronized (mLock) { 90 mPropertyHandlerMap.put(config.prop, handler); 91 mConfigs.put(config.prop, config); 92 } 93 } 94 addStaticProperty(VehiclePropConfig config, VehiclePropValue value)95 public void addStaticProperty(VehiclePropConfig config, VehiclePropValue value) { 96 synchronized (mLock) { 97 addProperty(config, new StaticPropertyHandler(value)); 98 } 99 } 100 waitForSubscriber(int propId, long timeoutMillis)101 public boolean waitForSubscriber(int propId, long timeoutMillis) { 102 long startTime = SystemClock.elapsedRealtime(); 103 try { 104 synchronized (mLock) { 105 while (mSubscribers.get(propId) == null) { 106 long waitMillis = startTime - SystemClock.elapsedRealtime() + timeoutMillis; 107 if (waitMillis < 0) break; 108 wait(waitMillis); 109 } 110 return mSubscribers.get(propId) != null; 111 } 112 } catch (InterruptedException e) { 113 Thread.currentThread().interrupt(); 114 return false; 115 } 116 } 117 injectEvent(VehiclePropValue value, boolean setProperty)118 public void injectEvent(VehiclePropValue value, boolean setProperty) { 119 synchronized (mLock) { 120 List<IVehicleCallback> callbacks = mSubscribers.get(value.prop); 121 assertNotNull("Injecting event failed for property: " + value.prop 122 + ". No listeners found", callbacks); 123 124 if (setProperty) { 125 // Update property if requested 126 VehicleHalPropertyHandler handler = mPropertyHandlerMap.get(value.prop); 127 if (handler != null) { 128 handler.onPropertySet(value); 129 } 130 } 131 132 for (int i = 0; i < callbacks.size(); i++) { 133 IVehicleCallback callback = callbacks.get(i); 134 try { 135 ArrayList<VehiclePropValue> values = new ArrayList<>(1); 136 values.add(value); 137 callback.onPropertyEvent(values); 138 } catch (RemoteException e) { 139 Log.e(TAG, "Failed invoking callback", e); 140 fail("Remote exception while injecting events."); 141 } 142 } 143 } 144 } 145 injectEvent(VehiclePropValue value)146 public void injectEvent(VehiclePropValue value) { 147 injectEvent(value, false); 148 } 149 injectError(int errorCode, int propertyId, int areaId)150 public void injectError(int errorCode, int propertyId, int areaId) { 151 synchronized (mLock) { 152 List<IVehicleCallback> callbacks = mSubscribers.get(propertyId); 153 assertNotNull("Injecting error failed for property: " + propertyId 154 + ". No listeners found", callbacks); 155 for (int i = 0; i < callbacks.size(); i++) { 156 IVehicleCallback callback = callbacks.get(i); 157 try { 158 callback.onPropertySetError(errorCode, propertyId, areaId); 159 } catch (RemoteException e) { 160 Log.e(TAG, "Failed invoking callback", e); 161 fail("Remote exception while injecting errors."); 162 } 163 } 164 } 165 } 166 167 @Override getAllPropConfigs()168 public ArrayList<VehiclePropConfig> getAllPropConfigs() { 169 synchronized (mLock) { 170 return new ArrayList<>(mConfigs.values()); 171 } 172 } 173 174 @Override getPropConfigs(ArrayList<Integer> props, getPropConfigsCallback cb)175 public void getPropConfigs(ArrayList<Integer> props, getPropConfigsCallback cb) { 176 synchronized (mLock) { 177 ArrayList<VehiclePropConfig> res = new ArrayList<>(); 178 for (Integer prop : props) { 179 VehiclePropConfig config = mConfigs.get(prop); 180 if (config == null) { 181 cb.onValues(StatusCode.INVALID_ARG, new ArrayList<>()); 182 return; 183 } 184 res.add(config); 185 } 186 cb.onValues(StatusCode.OK, res); 187 } 188 } 189 190 @Override get(VehiclePropValue requestedPropValue, getCallback cb)191 public void get(VehiclePropValue requestedPropValue, getCallback cb) { 192 synchronized (mLock) { 193 VehicleHalPropertyHandler handler = mPropertyHandlerMap.get(requestedPropValue.prop); 194 if (handler == null) { 195 cb.onValues(StatusCode.INVALID_ARG, null); 196 } else { 197 try { 198 VehiclePropValue prop = handler.onPropertyGet(requestedPropValue); 199 cb.onValues(StatusCode.OK, prop); 200 } catch (ServiceSpecificException e) { 201 // Don't directly pass ServiceSpecificException through binder to client, pass 202 // status code similar to how the c++ server does. 203 cb.onValues(e.errorCode, null); 204 } 205 } 206 } 207 } 208 209 @Override set(VehiclePropValue propValue)210 public int set(VehiclePropValue propValue) { 211 synchronized (mLock) { 212 VehicleHalPropertyHandler handler = mPropertyHandlerMap.get(propValue.prop); 213 if (handler == null) { 214 return StatusCode.INVALID_ARG; 215 } else { 216 try { 217 handler.onPropertySet(propValue); 218 return StatusCode.OK; 219 } catch (ServiceSpecificException e) { 220 // Don't directly pass ServiceSpecificException through binder to client, pass 221 // status code similar to how the c++ server does. 222 return e.errorCode; 223 } 224 } 225 } 226 } 227 228 @Override subscribe(IVehicleCallback callback, ArrayList<SubscribeOptions> options)229 public int subscribe(IVehicleCallback callback, ArrayList<SubscribeOptions> options) { 230 synchronized (mLock) { 231 for (SubscribeOptions opt : options) { 232 VehicleHalPropertyHandler handler = mPropertyHandlerMap.get(opt.propId); 233 if (handler == null) { 234 return StatusCode.INVALID_ARG; 235 } 236 237 handler.onPropertySubscribe(opt.propId, opt.sampleRate); 238 List<IVehicleCallback> subscribers = mSubscribers.get(opt.propId); 239 if (subscribers == null) { 240 subscribers = new ArrayList<>(); 241 mSubscribers.put(opt.propId, subscribers); 242 notifyAll(); 243 } else { 244 for (int i = 0; i < subscribers.size(); i++) { 245 IVehicleCallback s = subscribers.get(i); 246 if (callback.asBinder() == s.asBinder()) { 247 // Remove callback that was registered previously for this property 248 subscribers.remove(callback); 249 break; 250 } 251 } 252 } 253 subscribers.add(callback); 254 } 255 } 256 return StatusCode.OK; 257 } 258 259 @Override unsubscribe(IVehicleCallback callback, int propId)260 public int unsubscribe(IVehicleCallback callback, int propId) { 261 synchronized (mLock) { 262 VehicleHalPropertyHandler handler = mPropertyHandlerMap.get(propId); 263 if (handler == null) { 264 return StatusCode.INVALID_ARG; 265 } 266 267 handler.onPropertyUnsubscribe(propId); 268 List<IVehicleCallback> subscribers = mSubscribers.get(propId); 269 if (subscribers != null) { 270 subscribers.remove(callback); 271 if (subscribers.size() == 0) { 272 mSubscribers.remove(propId); 273 } 274 } 275 } 276 return StatusCode.OK; 277 } 278 279 @Override debugDump()280 public String debugDump() { 281 return null; 282 } 283 284 public static class FailingPropertyHandler implements VehicleHalPropertyHandler { 285 @Override onPropertySet(VehiclePropValue value)286 public void onPropertySet(VehiclePropValue value) { 287 fail("Unexpected onPropertySet call"); 288 } 289 290 @Override onPropertyGet(VehiclePropValue value)291 public VehiclePropValue onPropertyGet(VehiclePropValue value) { 292 fail("Unexpected onPropertyGet call"); 293 return null; 294 } 295 296 @Override onPropertySubscribe(int property, float sampleRate)297 public void onPropertySubscribe(int property, float sampleRate) { 298 fail("Unexpected onPropertySubscribe call"); 299 } 300 301 @Override onPropertyUnsubscribe(int property)302 public void onPropertyUnsubscribe(int property) { 303 fail("Unexpected onPropertyUnsubscribe call"); 304 } 305 } 306 307 @NotThreadSafe 308 public static final class StaticPropertyHandler extends FailingPropertyHandler { 309 310 private final VehiclePropValue mValue; 311 StaticPropertyHandler(VehiclePropValue value)312 public StaticPropertyHandler(VehiclePropValue value) { 313 mValue = value; 314 } 315 316 @Override onPropertyGet(VehiclePropValue value)317 public VehiclePropValue onPropertyGet(VehiclePropValue value) { 318 return mValue; 319 } 320 } 321 322 @NotThreadSafe 323 public static final class ErrorCodeHandler extends FailingPropertyHandler { 324 325 private final Object mLock = new Object(); 326 327 @GuardedBy("mLock") 328 private int mStatus; 329 setStatus(int status)330 public void setStatus(int status) { 331 synchronized (mLock) { 332 mStatus = status; 333 } 334 } 335 336 @Override onPropertyGet(VehiclePropValue value)337 public VehiclePropValue onPropertyGet(VehiclePropValue value) { 338 synchronized (mLock) { 339 throw new ServiceSpecificException(mStatus); 340 } 341 } 342 343 @Override onPropertySet(VehiclePropValue value)344 public void onPropertySet(VehiclePropValue value) { 345 synchronized (mLock) { 346 throw new ServiceSpecificException(mStatus); 347 } 348 } 349 } 350 351 @NotThreadSafe 352 public static final class DefaultPropertyHandler implements VehicleHalPropertyHandler { 353 354 private final VehiclePropConfig mConfig; 355 private VehiclePropValue mValue; 356 private boolean mSubscribed = false; 357 DefaultPropertyHandler(VehiclePropConfig config, VehiclePropValue initialValue)358 public DefaultPropertyHandler(VehiclePropConfig config, VehiclePropValue initialValue) { 359 mConfig = config; 360 mValue = initialValue; 361 } 362 363 @Override onPropertySet(VehiclePropValue value)364 public void onPropertySet(VehiclePropValue value) { 365 assertEquals(mConfig.prop, value.prop); 366 assertEquals(VehiclePropertyAccess.WRITE, 367 mConfig.access & VehiclePropertyAccess.WRITE); 368 mValue = value; 369 } 370 371 @Override onPropertyGet(VehiclePropValue value)372 public VehiclePropValue onPropertyGet(VehiclePropValue value) { 373 assertEquals(mConfig.prop, value.prop); 374 assertEquals(VehiclePropertyAccess.READ, 375 mConfig.access & VehiclePropertyAccess.READ); 376 return mValue; 377 } 378 379 @Override onPropertySubscribe(int property, float sampleRate)380 public void onPropertySubscribe(int property, float sampleRate) { 381 assertEquals(mConfig.prop, property); 382 mSubscribed = true; 383 } 384 385 @Override onPropertyUnsubscribe(int property)386 public void onPropertyUnsubscribe(int property) { 387 assertEquals(mConfig.prop, property); 388 if (!mSubscribed) { 389 throw new IllegalArgumentException("Property was not subscribed 0x" 390 + toHexString(property)); 391 } 392 mSubscribed = false; 393 } 394 } 395 } 396