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