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