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