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.hal; 18 19 import static com.android.car.CarServiceUtils.toByteArray; 20 import static com.android.car.CarServiceUtils.toFloatArray; 21 import static com.android.car.CarServiceUtils.toIntArray; 22 import static java.lang.Integer.toHexString; 23 24 import android.annotation.CheckResult; 25 import android.car.annotation.FutureFeature; 26 import android.hardware.automotive.vehicle.V2_0.IVehicle; 27 import android.hardware.automotive.vehicle.V2_0.IVehicleCallback; 28 import android.hardware.automotive.vehicle.V2_0.SubscribeFlags; 29 import android.hardware.automotive.vehicle.V2_0.SubscribeOptions; 30 import android.hardware.automotive.vehicle.V2_0.VehicleAreaConfig; 31 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig; 32 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 33 import android.hardware.automotive.vehicle.V2_0.VehicleProperty; 34 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess; 35 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode; 36 import android.os.HandlerThread; 37 import android.os.RemoteException; 38 import android.os.SystemClock; 39 import android.util.ArraySet; 40 import android.util.Log; 41 import android.util.SparseArray; 42 43 import com.google.android.collect.Lists; 44 45 import com.android.car.CarLog; 46 import com.android.car.internal.FeatureConfiguration; 47 import com.android.internal.annotations.VisibleForTesting; 48 49 import java.io.PrintWriter; 50 import java.lang.ref.WeakReference; 51 import java.util.ArrayList; 52 import java.util.Arrays; 53 import java.util.Collection; 54 import java.util.HashMap; 55 import java.util.HashSet; 56 import java.util.List; 57 import java.util.Set; 58 59 /** 60 * Abstraction for vehicle HAL. This class handles interface with native HAL and do basic parsing 61 * of received data (type check). Then each event is sent to corresponding {@link HalServiceBase} 62 * implementation. It is responsibility of {@link HalServiceBase} to convert data to corresponding 63 * Car*Service for Car*Manager API. 64 */ 65 public class VehicleHal extends IVehicleCallback.Stub { 66 67 private static final boolean DBG = false; 68 69 private static final int NO_AREA = -1; 70 71 private final HandlerThread mHandlerThread; 72 private final SensorHalService mSensorHal; 73 private final InfoHalService mInfoHal; 74 private final AudioHalService mAudioHal; 75 private final CabinHalService mCabinHal; 76 private final RadioHalService mRadioHal; 77 private final PowerHalService mPowerHal; 78 private final HvacHalService mHvacHal; 79 private final InputHalService mInputHal; 80 private final VendorExtensionHalService mVendorExtensionHal; 81 @FutureFeature 82 private VmsHalService mVmsHal; 83 84 @FutureFeature 85 private DiagnosticHalService mDiagnosticHal = null; 86 87 /** Might be re-assigned if Vehicle HAL is reconnected. */ 88 private volatile HalClient mHalClient; 89 90 /** Stores handler for each HAL property. Property events are sent to handler. */ 91 private final SparseArray<HalServiceBase> mPropertyHandlers = new SparseArray<>(); 92 /** This is for iterating all HalServices with fixed order. */ 93 private final ArrayList<HalServiceBase> mAllServices = new ArrayList<>(); 94 private final HashMap<Integer, SubscribeOptions> mSubscribedProperties = new HashMap<>(); 95 private final HashMap<Integer, VehiclePropConfig> mAllProperties = new HashMap<>(); 96 private final HashMap<Integer, VehiclePropertyEventInfo> mEventLog = new HashMap<>(); 97 VehicleHal(IVehicle vehicle)98 public VehicleHal(IVehicle vehicle) { 99 mHandlerThread = new HandlerThread("VEHICLE-HAL"); 100 mHandlerThread.start(); 101 // passing this should be safe as long as it is just kept and not used in constructor 102 mPowerHal = new PowerHalService(this); 103 mSensorHal = new SensorHalService(this); 104 mInfoHal = new InfoHalService(this); 105 mAudioHal = new AudioHalService(this); 106 mCabinHal = new CabinHalService(this); 107 mRadioHal = new RadioHalService(this); 108 mHvacHal = new HvacHalService(this); 109 mInputHal = new InputHalService(this); 110 mVendorExtensionHal = new VendorExtensionHalService(this); 111 if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) { 112 mVmsHal = new VmsHalService(this); 113 } 114 if(FeatureConfiguration.ENABLE_DIAGNOSTIC) { 115 mDiagnosticHal = new DiagnosticHalService(this); 116 } 117 mAllServices.addAll(Arrays.asList(mPowerHal, 118 mSensorHal, 119 mInfoHal, 120 mAudioHal, 121 mCabinHal, 122 mRadioHal, 123 mHvacHal, 124 mInputHal, 125 mVendorExtensionHal)); 126 if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) { 127 mAllServices.add(mVmsHal); 128 } 129 if(FeatureConfiguration.ENABLE_DIAGNOSTIC) { 130 mAllServices.add(mDiagnosticHal); 131 } 132 133 mHalClient = new HalClient(vehicle, mHandlerThread.getLooper(), this /*IVehicleCallback*/); 134 } 135 136 /** Dummy version only for testing */ 137 @VisibleForTesting VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal, AudioHalService audioHal, CabinHalService cabinHal, RadioHalService radioHal, HvacHalService hvacHal, HalClient halClient)138 public VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal, 139 AudioHalService audioHal, CabinHalService cabinHal, 140 RadioHalService radioHal, HvacHalService hvacHal, HalClient halClient) { 141 mHandlerThread = null; 142 mPowerHal = powerHal; 143 mSensorHal = sensorHal; 144 mInfoHal = infoHal; 145 mAudioHal = audioHal; 146 mCabinHal = cabinHal; 147 mRadioHal = radioHal; 148 mHvacHal = hvacHal; 149 mInputHal = null; 150 mVendorExtensionHal = null; 151 152 if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) { 153 // TODO(antoniocortes): do we need a test version of VmsHalService? 154 mVmsHal = null; 155 } 156 if(FeatureConfiguration.ENABLE_DIAGNOSTIC) { 157 mDiagnosticHal = null; 158 } 159 160 mHalClient = halClient; 161 } 162 163 /** Dummy version only for testing */ 164 @VisibleForTesting 165 @FutureFeature VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal, AudioHalService audioHal, CabinHalService cabinHal, DiagnosticHalService diagnosticHal, RadioHalService radioHal, HvacHalService hvacHal, HalClient halClient)166 public VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal, 167 AudioHalService audioHal, CabinHalService cabinHal, DiagnosticHalService diagnosticHal, 168 RadioHalService radioHal, HvacHalService hvacHal, HalClient halClient) { 169 mHandlerThread = null; 170 mPowerHal = powerHal; 171 mSensorHal = sensorHal; 172 mInfoHal = infoHal; 173 mAudioHal = audioHal; 174 mCabinHal = cabinHal; 175 mDiagnosticHal = diagnosticHal; 176 mRadioHal = radioHal; 177 mHvacHal = hvacHal; 178 mInputHal = null; 179 mVendorExtensionHal = null; 180 // TODO(antoniocortes): do we need a test version of VmsHalService? 181 mVmsHal = null; 182 mHalClient = halClient; 183 mDiagnosticHal = diagnosticHal; 184 } 185 vehicleHalReconnected(IVehicle vehicle)186 public void vehicleHalReconnected(IVehicle vehicle) { 187 synchronized (this) { 188 mHalClient = new HalClient(vehicle, mHandlerThread.getLooper(), 189 this /*IVehicleCallback*/); 190 191 SubscribeOptions[] options = mSubscribedProperties.values() 192 .toArray(new SubscribeOptions[0]); 193 194 try { 195 mHalClient.subscribe(options); 196 } catch (RemoteException e) { 197 throw new RuntimeException("Failed to subscribe: " + Arrays.asList(options), e); 198 } 199 } 200 } 201 init()202 public void init() { 203 Set<VehiclePropConfig> properties; 204 try { 205 properties = new HashSet<>(mHalClient.getAllPropConfigs()); 206 } catch (RemoteException e) { 207 throw new RuntimeException("Unable to retrieve vehicle property configuration", e); 208 } 209 210 synchronized (this) { 211 // Create map of all properties 212 for (VehiclePropConfig p : properties) { 213 mAllProperties.put(p.prop, p); 214 } 215 } 216 217 for (HalServiceBase service: mAllServices) { 218 Collection<VehiclePropConfig> taken = service.takeSupportedProperties(properties); 219 if (taken == null) { 220 continue; 221 } 222 if (DBG) { 223 Log.i(CarLog.TAG_HAL, "HalService " + service + " take properties " + taken.size()); 224 } 225 synchronized (this) { 226 for (VehiclePropConfig p: taken) { 227 mPropertyHandlers.append(p.prop, service); 228 } 229 } 230 properties.removeAll(taken); 231 service.init(); 232 } 233 } 234 release()235 public void release() { 236 // release in reverse order from init 237 for (int i = mAllServices.size() - 1; i >= 0; i--) { 238 mAllServices.get(i).release(); 239 } 240 synchronized (this) { 241 for (int p : mSubscribedProperties.keySet()) { 242 try { 243 mHalClient.unsubscribe(p); 244 } catch (RemoteException e) { 245 // Ignore exceptions on shutdown path. 246 Log.w(CarLog.TAG_HAL, "Failed to unsubscribe", e); 247 } 248 } 249 mSubscribedProperties.clear(); 250 mAllProperties.clear(); 251 } 252 // keep the looper thread as should be kept for the whole life cycle. 253 } 254 getSensorHal()255 public SensorHalService getSensorHal() { 256 return mSensorHal; 257 } 258 getInfoHal()259 public InfoHalService getInfoHal() { 260 return mInfoHal; 261 } 262 getAudioHal()263 public AudioHalService getAudioHal() { 264 return mAudioHal; 265 } 266 getCabinHal()267 public CabinHalService getCabinHal() { 268 return mCabinHal; 269 } 270 271 @FutureFeature getDiagnosticHal()272 public DiagnosticHalService getDiagnosticHal() { return mDiagnosticHal; } 273 getRadioHal()274 public RadioHalService getRadioHal() { 275 return mRadioHal; 276 } 277 getPowerHal()278 public PowerHalService getPowerHal() { 279 return mPowerHal; 280 } 281 getHvacHal()282 public HvacHalService getHvacHal() { 283 return mHvacHal; 284 } 285 getInputHal()286 public InputHalService getInputHal() { 287 return mInputHal; 288 } 289 getVendorExtensionHal()290 public VendorExtensionHalService getVendorExtensionHal() { 291 return mVendorExtensionHal; 292 } 293 294 @FutureFeature getVmsHal()295 public VmsHalService getVmsHal() { return mVmsHal; } 296 assertServiceOwnerLocked(HalServiceBase service, int property)297 private void assertServiceOwnerLocked(HalServiceBase service, int property) { 298 if (service != mPropertyHandlers.get(property)) { 299 throw new IllegalArgumentException("Property 0x" + toHexString(property) 300 + " is not owned by service: " + service); 301 } 302 } 303 304 /** 305 * Subscribes given properties with sampling rate defaults to 0 and no special flags provided. 306 * 307 * @see #subscribeProperty(HalServiceBase, int, float, int) 308 */ subscribeProperty(HalServiceBase service, int property)309 public void subscribeProperty(HalServiceBase service, int property) 310 throws IllegalArgumentException { 311 subscribeProperty(service, property, 0f, SubscribeFlags.DEFAULT); 312 } 313 314 /** 315 * Subscribes given properties with default subscribe flag. 316 * 317 * @see #subscribeProperty(HalServiceBase, int, float, int) 318 */ subscribeProperty(HalServiceBase service, int property, float sampleRateHz)319 public void subscribeProperty(HalServiceBase service, int property, float sampleRateHz) 320 throws IllegalArgumentException { 321 subscribeProperty(service, property, sampleRateHz, SubscribeFlags.DEFAULT); 322 } 323 324 /** 325 * Subscribe given property. Only Hal service owning the property can subscribe it. 326 * 327 * @param service HalService that owns this property 328 * @param property property id (VehicleProperty) 329 * @param samplingRateHz sampling rate in Hz for continuous properties 330 * @param flags flags from {@link android.hardware.automotive.vehicle.V2_0.SubscribeFlags} 331 * @throws IllegalArgumentException thrown if property is not supported by VHAL 332 */ subscribeProperty(HalServiceBase service, int property, float samplingRateHz, int flags)333 public void subscribeProperty(HalServiceBase service, int property, 334 float samplingRateHz, int flags) throws IllegalArgumentException { 335 if (DBG) { 336 Log.i(CarLog.TAG_HAL, "subscribeProperty, service:" + service 337 + ", property: 0x" + toHexString(property)); 338 } 339 VehiclePropConfig config; 340 synchronized (this) { 341 config = mAllProperties.get(property); 342 } 343 344 if (config == null) { 345 throw new IllegalArgumentException("subscribe error: config is null for property 0x" + 346 toHexString(property)); 347 } else if (isPropertySubscribable(config)) { 348 SubscribeOptions opts = new SubscribeOptions(); 349 opts.propId = property; 350 opts.sampleRate = samplingRateHz; 351 opts.flags = flags; 352 synchronized (this) { 353 assertServiceOwnerLocked(service, property); 354 mSubscribedProperties.put(property, opts); 355 } 356 try { 357 mHalClient.subscribe(opts); 358 } catch (RemoteException e) { 359 Log.e(CarLog.TAG_HAL, "Failed to subscribe to property: 0x" + property, e); 360 } 361 } else { 362 Log.e(CarLog.TAG_HAL, "Cannot subscribe to property: " + property); 363 } 364 } 365 unsubscribeProperty(HalServiceBase service, int property)366 public void unsubscribeProperty(HalServiceBase service, int property) { 367 if (DBG) { 368 Log.i(CarLog.TAG_HAL, "unsubscribeProperty, service:" + service 369 + ", property: 0x" + toHexString(property)); 370 } 371 VehiclePropConfig config; 372 synchronized (this) { 373 config = mAllProperties.get(property); 374 } 375 376 if (config == null) { 377 Log.e(CarLog.TAG_HAL, "unsubscribeProperty: property " + property + " does not exist"); 378 } else if (isPropertySubscribable(config)) { 379 synchronized (this) { 380 assertServiceOwnerLocked(service, property); 381 mSubscribedProperties.remove(property); 382 } 383 try { 384 mHalClient.unsubscribe(property); 385 } catch (RemoteException e) { 386 Log.e(CarLog.TAG_SERVICE, "Failed to unsubscribe from property: 0x" 387 + toHexString(property), e); 388 } 389 } else { 390 Log.e(CarLog.TAG_HAL, "Cannot unsubscribe property: " + property); 391 } 392 } 393 isPropertySupported(int propertyId)394 public boolean isPropertySupported(int propertyId) { 395 return mAllProperties.containsKey(propertyId); 396 } 397 getAllPropConfigs()398 public Collection<VehiclePropConfig> getAllPropConfigs() { 399 return mAllProperties.values(); 400 } 401 get(int propertyId)402 public VehiclePropValue get(int propertyId) throws PropertyTimeoutException { 403 return get(propertyId, NO_AREA); 404 } 405 get(int propertyId, int areaId)406 public VehiclePropValue get(int propertyId, int areaId) throws PropertyTimeoutException { 407 if (DBG) { 408 Log.i(CarLog.TAG_HAL, "get, property: 0x" + toHexString(propertyId) 409 + ", areaId: 0x" + toHexString(areaId)); 410 } 411 VehiclePropValue propValue = new VehiclePropValue(); 412 propValue.prop = propertyId; 413 propValue.areaId = areaId; 414 return mHalClient.getValue(propValue); 415 } 416 get(Class clazz, int propertyId)417 public <T> T get(Class clazz, int propertyId) throws PropertyTimeoutException { 418 return get(clazz, createPropValue(propertyId, NO_AREA)); 419 } 420 get(Class clazz, int propertyId, int areaId)421 public <T> T get(Class clazz, int propertyId, int areaId) throws PropertyTimeoutException { 422 return get(clazz, createPropValue(propertyId, areaId)); 423 } 424 425 @SuppressWarnings("unchecked") get(Class clazz, VehiclePropValue requestedPropValue)426 public <T> T get(Class clazz, VehiclePropValue requestedPropValue) 427 throws PropertyTimeoutException { 428 VehiclePropValue propValue; 429 propValue = mHalClient.getValue(requestedPropValue); 430 431 if (clazz == Integer.class || clazz == int.class) { 432 return (T) propValue.value.int32Values.get(0); 433 } else if (clazz == Boolean.class || clazz == boolean.class) { 434 return (T) Boolean.valueOf(propValue.value.int32Values.get(0) == 1); 435 } else if (clazz == Float.class || clazz == float.class) { 436 return (T) propValue.value.floatValues.get(0); 437 } else if (clazz == Integer[].class) { 438 Integer[] intArray = new Integer[propValue.value.int32Values.size()]; 439 return (T) propValue.value.int32Values.toArray(intArray); 440 } else if (clazz == Float[].class) { 441 Float[] floatArray = new Float[propValue.value.floatValues.size()]; 442 return (T) propValue.value.floatValues.toArray(floatArray); 443 } else if (clazz == int[].class) { 444 return (T) toIntArray(propValue.value.int32Values); 445 } else if (clazz == float[].class) { 446 return (T) toFloatArray(propValue.value.floatValues); 447 } else if (clazz == byte[].class) { 448 return (T) toByteArray(propValue.value.bytes); 449 } else if (clazz == String.class) { 450 return (T) propValue.value.stringValue; 451 } else { 452 throw new IllegalArgumentException("Unexpected type: " + clazz); 453 } 454 } 455 get(VehiclePropValue requestedPropValue)456 public VehiclePropValue get(VehiclePropValue requestedPropValue) 457 throws PropertyTimeoutException { 458 return mHalClient.getValue(requestedPropValue); 459 } 460 set(VehiclePropValue propValue)461 void set(VehiclePropValue propValue) throws PropertyTimeoutException { 462 mHalClient.setValue(propValue); 463 } 464 465 @CheckResult set(int propId)466 VehiclePropValueSetter set(int propId) { 467 return new VehiclePropValueSetter(mHalClient, propId, NO_AREA); 468 } 469 470 @CheckResult set(int propId, int areaId)471 VehiclePropValueSetter set(int propId, int areaId) { 472 return new VehiclePropValueSetter(mHalClient, propId, areaId); 473 } 474 isPropertySubscribable(VehiclePropConfig config)475 static boolean isPropertySubscribable(VehiclePropConfig config) { 476 if ((config.access & VehiclePropertyAccess.READ) == 0 || 477 (config.changeMode == VehiclePropertyChangeMode.STATIC)) { 478 return false; 479 } 480 return true; 481 } 482 dumpProperties(PrintWriter writer, Collection<VehiclePropConfig> configs)483 static void dumpProperties(PrintWriter writer, Collection<VehiclePropConfig> configs) { 484 for (VehiclePropConfig config : configs) { 485 writer.println(String.format("property 0x%x", config.prop)); 486 } 487 } 488 489 private final ArraySet<HalServiceBase> mServicesToDispatch = new ArraySet<>(); 490 491 @Override onPropertyEvent(ArrayList<VehiclePropValue> propValues)492 public void onPropertyEvent(ArrayList<VehiclePropValue> propValues) { 493 synchronized (this) { 494 for (VehiclePropValue v : propValues) { 495 HalServiceBase service = mPropertyHandlers.get(v.prop); 496 if(service == null) { 497 Log.e(CarLog.TAG_HAL, "HalService not found for prop: 0x" 498 + toHexString(v.prop)); 499 continue; 500 } 501 service.getDispatchList().add(v); 502 mServicesToDispatch.add(service); 503 VehiclePropertyEventInfo info = mEventLog.get(v.prop); 504 if (info == null) { 505 info = new VehiclePropertyEventInfo(v); 506 mEventLog.put(v.prop, info); 507 } else { 508 info.addNewEvent(v); 509 } 510 } 511 } 512 for (HalServiceBase s : mServicesToDispatch) { 513 s.handleHalEvents(s.getDispatchList()); 514 s.getDispatchList().clear(); 515 } 516 mServicesToDispatch.clear(); 517 } 518 519 @Override onPropertySet(VehiclePropValue value)520 public void onPropertySet(VehiclePropValue value) { 521 // No need to handle on-property-set events in HAL service yet. 522 } 523 524 @Override onPropertySetError(int errorCode, int propId, int areaId)525 public void onPropertySetError(int errorCode, int propId, int areaId) { 526 Log.e(CarLog.TAG_HAL, String.format("onPropertySetError, errorCode: %d, prop: 0x%x, " 527 + "area: 0x%x", errorCode, propId, areaId)); 528 if (propId != VehicleProperty.INVALID) { 529 HalServiceBase service = mPropertyHandlers.get(propId); 530 if (service != null) { 531 service.handlePropertySetError(propId, areaId); 532 } 533 } 534 } 535 dump(PrintWriter writer)536 public void dump(PrintWriter writer) { 537 writer.println("**dump HAL services**"); 538 for (HalServiceBase service: mAllServices) { 539 service.dump(writer); 540 } 541 542 List<VehiclePropConfig> configList; 543 synchronized (this) { 544 configList = new ArrayList<>(mAllProperties.values()); 545 } 546 547 writer.println("**All properties**"); 548 for (VehiclePropConfig config : configList) { 549 StringBuilder builder = new StringBuilder() 550 .append("Property:0x").append(toHexString(config.prop)) 551 .append(",access:0x").append(toHexString(config.access)) 552 .append(",changeMode:0x").append(toHexString(config.changeMode)) 553 .append(",areas:0x").append(toHexString(config.supportedAreas)) 554 .append(",config:0x").append(Arrays.toString(config.configArray.toArray())) 555 .append(",fs min:").append(config.minSampleRate) 556 .append(",fs max:").append(config.maxSampleRate); 557 for (VehicleAreaConfig area : config.areaConfigs) { 558 builder.append(",areaId :").append(toHexString(area.areaId)) 559 .append(",f min:").append(area.minFloatValue) 560 .append(",f max:").append(area.maxFloatValue) 561 .append(",i min:").append(area.minInt32Value) 562 .append(",i max:").append(area.maxInt32Value) 563 .append(",i64 min:").append(area.minInt64Value) 564 .append(",i64 max:").append(area.maxInt64Value); 565 } 566 writer.println(builder.toString()); 567 } 568 writer.println(String.format("**All Events, now ns:%d**", 569 SystemClock.elapsedRealtimeNanos())); 570 for (VehiclePropertyEventInfo info : mEventLog.values()) { 571 writer.println(String.format("event count:%d, lastEvent:%s", 572 info.eventCount, dumpVehiclePropValue(info.lastEvent))); 573 } 574 575 writer.println("**Property handlers**"); 576 for (int i = 0; i < mPropertyHandlers.size(); i++) { 577 int propId = mPropertyHandlers.keyAt(i); 578 HalServiceBase service = mPropertyHandlers.valueAt(i); 579 writer.println(String.format("Prop: 0x%08X, service: %s", propId, service)); 580 } 581 } 582 583 /** 584 * Inject a fake boolean HAL event - for testing purposes. 585 * @param propId - VehicleProperty ID 586 * @param areaId - Vehicle Area ID 587 * @param value - true/false to inject 588 */ injectBooleanEvent(int propId, int areaId, boolean value)589 public void injectBooleanEvent(int propId, int areaId, boolean value) { 590 VehiclePropValue v = createPropValue(propId, areaId); 591 v.value.int32Values.add(value? 1 : 0); 592 onPropertyEvent(Lists.newArrayList(v)); 593 } 594 595 /** 596 * Inject a fake Integer HAL event - for testing purposes. 597 * @param propId - VehicleProperty ID 598 * @param value - Integer value to inject 599 */ injectIntegerEvent(int propId, int value)600 public void injectIntegerEvent(int propId, int value) { 601 VehiclePropValue v = createPropValue(propId, 0); 602 v.value.int32Values.add(value); 603 v.timestamp = SystemClock.elapsedRealtimeNanos(); 604 onPropertyEvent(Lists.newArrayList(v)); 605 } 606 607 private static class VehiclePropertyEventInfo { 608 private int eventCount; 609 private VehiclePropValue lastEvent; 610 VehiclePropertyEventInfo(VehiclePropValue event)611 private VehiclePropertyEventInfo(VehiclePropValue event) { 612 eventCount = 1; 613 lastEvent = event; 614 } 615 addNewEvent(VehiclePropValue event)616 private void addNewEvent(VehiclePropValue event) { 617 eventCount++; 618 lastEvent = event; 619 } 620 } 621 622 final class VehiclePropValueSetter { 623 final WeakReference<HalClient> mClient; 624 final VehiclePropValue mPropValue; 625 VehiclePropValueSetter(HalClient client, int propId, int areaId)626 private VehiclePropValueSetter(HalClient client, int propId, int areaId) { 627 mClient = new WeakReference<>(client); 628 mPropValue = new VehiclePropValue(); 629 mPropValue.prop = propId; 630 mPropValue.areaId = areaId; 631 } 632 to(boolean value)633 void to(boolean value) throws PropertyTimeoutException { 634 to(value ? 1 : 0); 635 } 636 to(int value)637 void to(int value) throws PropertyTimeoutException { 638 mPropValue.value.int32Values.add(value); 639 submit(); 640 } 641 to(int[] values)642 void to(int[] values) throws PropertyTimeoutException { 643 for (int value : values) { 644 mPropValue.value.int32Values.add(value); 645 } 646 submit(); 647 } 648 to(Collection<Integer> values)649 void to(Collection<Integer> values) throws PropertyTimeoutException { 650 mPropValue.value.int32Values.addAll(values); 651 submit(); 652 } 653 submit()654 void submit() throws PropertyTimeoutException { 655 HalClient client = mClient.get(); 656 if (client != null) { 657 if (DBG) { 658 Log.i(CarLog.TAG_HAL, "set, property: 0x" + toHexString(mPropValue.prop) 659 + ", areaId: 0x" + toHexString(mPropValue.areaId)); 660 } 661 client.setValue(mPropValue); 662 } 663 } 664 } 665 dumpVehiclePropValue(VehiclePropValue value)666 private static String dumpVehiclePropValue(VehiclePropValue value) { 667 final int MAX_BYTE_SIZE = 20; 668 669 StringBuilder sb = new StringBuilder() 670 .append("Property:0x").append(toHexString(value.prop)) 671 .append(",timestamp:").append(value.timestamp) 672 .append(",zone:0x").append(toHexString(value.areaId)) 673 .append(",floatValues: ").append(Arrays.toString(value.value.floatValues.toArray())) 674 .append(",int32Values: ").append(Arrays.toString(value.value.int32Values.toArray())) 675 .append(",int64Values: ") 676 .append(Arrays.toString(value.value.int64Values.toArray())); 677 678 if (value.value.bytes.size() > MAX_BYTE_SIZE) { 679 Object[] bytes = Arrays.copyOf(value.value.bytes.toArray(), MAX_BYTE_SIZE); 680 sb.append(",bytes: ").append(Arrays.toString(bytes)); 681 } else { 682 sb.append(",bytes: ").append(Arrays.toString(value.value.bytes.toArray())); 683 } 684 sb.append(",string: ").append(value.value.stringValue); 685 686 return sb.toString(); 687 } 688 createPropValue(int propId, int areaId)689 private static VehiclePropValue createPropValue(int propId, int areaId) { 690 VehiclePropValue propValue = new VehiclePropValue(); 691 propValue.prop = propId; 692 propValue.areaId = areaId; 693 return propValue; 694 } 695 } 696