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