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