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 android.os.SystemClock.uptimeMillis; 20 21 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE; 22 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 23 24 import android.annotation.CheckResult; 25 import android.annotation.Nullable; 26 import android.car.VehiclePropertyIds; 27 import android.car.builtin.os.TraceHelper; 28 import android.car.builtin.util.Slogf; 29 import android.content.Context; 30 import android.hardware.automotive.vehicle.StatusCode; 31 import android.hardware.automotive.vehicle.SubscribeOptions; 32 import android.hardware.automotive.vehicle.VehiclePropError; 33 import android.hardware.automotive.vehicle.VehicleProperty; 34 import android.hardware.automotive.vehicle.VehiclePropertyAccess; 35 import android.hardware.automotive.vehicle.VehiclePropertyChangeMode; 36 import android.hardware.automotive.vehicle.VehiclePropertyStatus; 37 import android.hardware.automotive.vehicle.VehiclePropertyType; 38 import android.os.Handler; 39 import android.os.HandlerThread; 40 import android.os.ParcelFileDescriptor; 41 import android.os.RemoteException; 42 import android.os.ServiceSpecificException; 43 import android.os.SystemClock; 44 import android.os.Trace; 45 import android.util.ArrayMap; 46 import android.util.ArraySet; 47 import android.util.Pair; 48 import android.util.SparseArray; 49 50 import com.android.car.CarLog; 51 import com.android.car.CarServiceUtils; 52 import com.android.car.CarSystemService; 53 import com.android.car.VehicleStub; 54 import com.android.car.VehicleStub.SubscriptionClient; 55 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 56 import com.android.car.internal.util.IndentingPrintWriter; 57 import com.android.car.internal.util.Lists; 58 import com.android.internal.annotations.GuardedBy; 59 import com.android.internal.annotations.VisibleForTesting; 60 61 import java.io.PrintWriter; 62 import java.util.ArrayList; 63 import java.util.Arrays; 64 import java.util.Collection; 65 import java.util.List; 66 import java.util.Map; 67 import java.util.Timer; 68 import java.util.TimerTask; 69 import java.util.concurrent.TimeUnit; 70 import java.util.stream.Collectors; 71 72 /** 73 * Abstraction for vehicle HAL. This class handles interface with native HAL and does basic parsing 74 * of received data (type check). Then each event is sent to corresponding {@link HalServiceBase} 75 * implementation. It is the responsibility of {@link HalServiceBase} to convert data to 76 * corresponding Car*Service for Car*Manager API. 77 */ 78 public class VehicleHal implements VehicleHalCallback, CarSystemService { 79 80 private static final boolean DBG = false; 81 private static final long TRACE_TAG = TraceHelper.TRACE_TAG_CAR_SERVICE; 82 83 /** 84 * Used in {@link VehicleHal#dumpPropValue} method when copying 85 * {@link HalPropValue}. 86 */ 87 private static final int MAX_BYTE_SIZE = 20; 88 89 public static final int NO_AREA = -1; 90 public static final float NO_SAMPLE_RATE = -1; 91 92 /** 93 * If call to vehicle HAL returns StatusCode.TRY_AGAIN, we will retry to invoke that method 94 * again for this amount of milliseconds. 95 */ 96 private static final int MAX_DURATION_FOR_RETRIABLE_RESULT_MS = 2000; 97 98 private static final int SLEEP_BETWEEN_RETRIABLE_INVOKES_MS = 100; 99 private static final float PRECISION_THRESHOLD = 0.001f; 100 101 private final HandlerThread mHandlerThread; 102 private final Handler mHandler; 103 private final SubscriptionClient mSubscriptionClient; 104 105 private final PowerHalService mPowerHal; 106 private final PropertyHalService mPropertyHal; 107 private final InputHalService mInputHal; 108 private final VmsHalService mVmsHal; 109 private final UserHalService mUserHal; 110 private final DiagnosticHalService mDiagnosticHal; 111 private final ClusterHalService mClusterHalService; 112 private final EvsHalService mEvsHal; 113 private final TimeHalService mTimeHalService; 114 private final HalPropValueBuilder mPropValueBuilder; 115 private final VehicleStub mVehicleStub; 116 117 private final Object mLock = new Object(); 118 119 // Only changed for test. 120 private int mMaxDurationForRetryMs = MAX_DURATION_FOR_RETRIABLE_RESULT_MS; 121 // Only changed for test. 122 private int mSleepBetweenRetryMs = SLEEP_BETWEEN_RETRIABLE_INVOKES_MS; 123 124 /** Stores handler for each HAL property. Property events are sent to handler. */ 125 @GuardedBy("mLock") 126 private final SparseArray<HalServiceBase> mPropertyHandlers = new SparseArray<>(); 127 /** This is for iterating all HalServices with fixed order. */ 128 @GuardedBy("mLock") 129 private final List<HalServiceBase> mAllServices; 130 @GuardedBy("mLock") 131 private final ArrayMap<Pair<Integer, Integer>, Float> mUpdateRateByPropIdAreadId = 132 new ArrayMap<>(); 133 @GuardedBy("mLock") 134 private final SparseArray<HalPropConfig> mAllProperties = new SparseArray<>(); 135 136 @GuardedBy("mLock") 137 private final SparseArray<VehiclePropertyEventInfo> mEventLog = new SparseArray<>(); 138 139 // Used by injectVHALEvent for testing purposes. Delimiter for an array of data 140 private static final String DATA_DELIMITER = ","; 141 142 /** 143 * Constructs a new {@link VehicleHal} object given the {@link Context} and {@link IVehicle} 144 * both passed as parameters. 145 */ VehicleHal(Context context, VehicleStub vehicle)146 public VehicleHal(Context context, VehicleStub vehicle) { 147 this(context, /* powerHal= */ null, /* propertyHal= */ null, 148 /* inputHal= */ null, /* vmsHal= */ null, /* userHal= */ null, 149 /* diagnosticHal= */ null, /* clusterHalService= */ null, 150 /* timeHalService= */ null, 151 CarServiceUtils.getHandlerThread(VehicleHal.class.getSimpleName()), vehicle); 152 } 153 154 /** 155 * Constructs a new {@link VehicleHal} object given the services passed as parameters. 156 * This method must be used by tests only. 157 */ 158 @VisibleForTesting VehicleHal(Context context, PowerHalService powerHal, PropertyHalService propertyHal, InputHalService inputHal, VmsHalService vmsHal, UserHalService userHal, DiagnosticHalService diagnosticHal, ClusterHalService clusterHalService, TimeHalService timeHalService, HandlerThread handlerThread, VehicleStub vehicle)159 VehicleHal(Context context, 160 PowerHalService powerHal, 161 PropertyHalService propertyHal, 162 InputHalService inputHal, 163 VmsHalService vmsHal, 164 UserHalService userHal, 165 DiagnosticHalService diagnosticHal, 166 ClusterHalService clusterHalService, 167 TimeHalService timeHalService, 168 HandlerThread handlerThread, 169 VehicleStub vehicle) { 170 // Must be initialized before HalService so that HalService could use this. 171 mPropValueBuilder = vehicle.getHalPropValueBuilder(); 172 mHandlerThread = handlerThread; 173 mHandler = new Handler(mHandlerThread.getLooper()); 174 mPowerHal = powerHal != null ? powerHal : new PowerHalService(context, this); 175 mPropertyHal = propertyHal != null ? propertyHal : new PropertyHalService(this); 176 mInputHal = inputHal != null ? inputHal : new InputHalService(this); 177 mVmsHal = vmsHal != null ? vmsHal : new VmsHalService(context, this); 178 mUserHal = userHal != null ? userHal : new UserHalService(this); 179 mDiagnosticHal = diagnosticHal != null ? diagnosticHal : new DiagnosticHalService(this); 180 mClusterHalService = clusterHalService != null 181 ? clusterHalService : new ClusterHalService(this); 182 mEvsHal = new EvsHalService(this); 183 mTimeHalService = timeHalService != null 184 ? timeHalService : new TimeHalService(context, this); 185 mAllServices = List.of( 186 mPowerHal, 187 mInputHal, 188 mDiagnosticHal, 189 mVmsHal, 190 mUserHal, 191 mClusterHalService, 192 mEvsHal, 193 mTimeHalService, 194 // mPropertyHal must be the last so that on init/release it can be used for all 195 // other HAL services properties. 196 mPropertyHal); 197 mVehicleStub = vehicle; 198 mSubscriptionClient = vehicle.newSubscriptionClient(this); 199 } 200 201 @VisibleForTesting setMaxDurationForRetryMs(int maxDurationForRetryMs)202 void setMaxDurationForRetryMs(int maxDurationForRetryMs) { 203 mMaxDurationForRetryMs = maxDurationForRetryMs; 204 } 205 206 @VisibleForTesting setSleepBetweenRetryMs(int sleepBetweenRetryMs)207 void setSleepBetweenRetryMs(int sleepBetweenRetryMs) { 208 mSleepBetweenRetryMs = sleepBetweenRetryMs; 209 } 210 fetchAllPropConfigs()211 private void fetchAllPropConfigs() { 212 synchronized (mLock) { 213 if (mAllProperties.size() != 0) { // already set 214 Slogf.i(CarLog.TAG_HAL, "fetchAllPropConfigs already fetched"); 215 return; 216 } 217 } 218 HalPropConfig[] configs; 219 try { 220 configs = mVehicleStub.getAllPropConfigs(); 221 if (configs == null || configs.length == 0) { 222 Slogf.e(CarLog.TAG_HAL, "getAllPropConfigs returned empty configs"); 223 return; 224 } 225 } catch (RemoteException | ServiceSpecificException e) { 226 throw new RuntimeException("Unable to retrieve vehicle property configuration", e); 227 } 228 229 synchronized (mLock) { 230 // Create map of all properties 231 for (HalPropConfig p : configs) { 232 if (DBG) { 233 Slogf.i(CarLog.TAG_HAL, "Add config for prop: 0x%x config: %s", p.getPropId(), 234 p.toString()); 235 } 236 mAllProperties.put(p.getPropId(), p); 237 } 238 } 239 } 240 handleOnPropertyEvent(List<HalPropValue> propValues)241 private void handleOnPropertyEvent(List<HalPropValue> propValues) { 242 synchronized (mLock) { 243 for (int i = 0; i < propValues.size(); i++) { 244 HalPropValue v = propValues.get(i); 245 int propId = v.getPropId(); 246 HalServiceBase service = mPropertyHandlers.get(propId); 247 if (service == null) { 248 Slogf.e(CarLog.TAG_HAL, 249 "handleOnPropertyEvent: HalService not found for prop: 0x%x", propId); 250 continue; 251 } 252 service.getDispatchList().add(v); 253 mServicesToDispatch.add(service); 254 VehiclePropertyEventInfo info = mEventLog.get(propId); 255 if (info == null) { 256 info = new VehiclePropertyEventInfo(v); 257 mEventLog.put(propId, info); 258 } else { 259 info.addNewEvent(v); 260 } 261 } 262 } 263 for (HalServiceBase s : mServicesToDispatch) { 264 s.onHalEvents(s.getDispatchList()); 265 s.getDispatchList().clear(); 266 } 267 mServicesToDispatch.clear(); 268 } 269 handleOnPropertySetError(List<VehiclePropError> errors)270 private void handleOnPropertySetError(List<VehiclePropError> errors) { 271 SparseArray<ArrayList<VehiclePropError>> errorsByPropId = 272 new SparseArray<ArrayList<VehiclePropError>>(); 273 for (int i = 0; i < errors.size(); i++) { 274 VehiclePropError error = errors.get(i); 275 int errorCode = error.errorCode; 276 int propId = error.propId; 277 int areaId = error.areaId; 278 Slogf.w(CarLog.TAG_HAL, "onPropertySetError, errorCode: %d, prop: 0x%x, area: 0x%x", 279 errorCode, propId, areaId); 280 if (propId == VehicleProperty.INVALID) { 281 continue; 282 } 283 284 ArrayList<VehiclePropError> propErrors; 285 if (errorsByPropId.get(propId) == null) { 286 propErrors = new ArrayList<VehiclePropError>(); 287 errorsByPropId.put(propId, propErrors); 288 } else { 289 propErrors = errorsByPropId.get(propId); 290 } 291 propErrors.add(error); 292 } 293 294 for (int i = 0; i < errorsByPropId.size(); i++) { 295 int propId = errorsByPropId.keyAt(i); 296 HalServiceBase service; 297 synchronized (mLock) { 298 service = mPropertyHandlers.get(propId); 299 } 300 if (service == null) { 301 Slogf.e(CarLog.TAG_HAL, 302 "handleOnPropertySetError: HalService not found for prop: 0x%x", propId); 303 continue; 304 } 305 306 ArrayList<VehiclePropError> propErrors = errorsByPropId.get(propId); 307 service.onPropertySetError(propErrors); 308 } 309 } 310 errorMessage(String action, HalPropValue propValue, String errorMsg)311 private static String errorMessage(String action, HalPropValue propValue, String errorMsg) { 312 return String.format("Failed to %s value for: 0x%x, areaId: 0x%x, error: %s", action, 313 propValue.getPropId(), propValue.getAreaId(), errorMsg); 314 } 315 getValueWithRetry(HalPropValue value)316 private HalPropValue getValueWithRetry(HalPropValue value) { 317 return getValueWithRetry(value, /* maxRetries= */ 0); 318 } 319 getValueWithRetry(HalPropValue value, int maxRetries)320 private HalPropValue getValueWithRetry(HalPropValue value, int maxRetries) { 321 HalPropValue result; 322 Trace.traceBegin(TRACE_TAG, "VehicleStub#getValueWithRetry"); 323 try { 324 result = invokeRetriable((requestValue) -> { 325 Trace.traceBegin(TRACE_TAG, "VehicleStub#get"); 326 try { 327 return mVehicleStub.get(requestValue); 328 } finally { 329 Trace.traceEnd(TRACE_TAG); 330 } 331 }, "get", value, mMaxDurationForRetryMs, mSleepBetweenRetryMs, maxRetries); 332 } finally { 333 Trace.traceEnd(TRACE_TAG); 334 } 335 336 if (result == null) { 337 // If VHAL returns null result, but the status is OKAY. We treat that as NOT_AVAILABLE. 338 throw new ServiceSpecificException(StatusCode.NOT_AVAILABLE, 339 errorMessage("get", value, "VHAL returns null for property value")); 340 } 341 return result; 342 } 343 setValueWithRetry(HalPropValue value)344 private void setValueWithRetry(HalPropValue value) { 345 invokeRetriable((requestValue) -> { 346 Trace.traceBegin(TRACE_TAG, "VehicleStub#set"); 347 mVehicleStub.set(requestValue); 348 Trace.traceEnd(TRACE_TAG); 349 return null; 350 }, "set", value, mMaxDurationForRetryMs, mSleepBetweenRetryMs, /* maxRetries= */ 0); 351 } 352 353 /** 354 * Inits the vhal configurations. 355 */ 356 @Override init()357 public void init() { 358 // nothing to init as everything was done on priorityInit 359 } 360 361 /** 362 * PriorityInit for the vhal configurations. 363 */ priorityInit()364 public void priorityInit() { 365 fetchAllPropConfigs(); 366 367 // PropertyHalService will take most properties, so make it big enough. 368 ArrayMap<HalServiceBase, ArrayList<HalPropConfig>> configsForAllServices; 369 synchronized (mLock) { 370 configsForAllServices = new ArrayMap<>(mAllServices.size()); 371 for (int i = 0; i < mAllServices.size(); i++) { 372 ArrayList<HalPropConfig> configsForService = new ArrayList(); 373 HalServiceBase service = mAllServices.get(i); 374 configsForAllServices.put(service, configsForService); 375 int[] supportedProps = service.getAllSupportedProperties(); 376 if (supportedProps.length == 0) { 377 for (int j = 0; j < mAllProperties.size(); j++) { 378 Integer propId = mAllProperties.keyAt(j); 379 if (service.isSupportedProperty(propId)) { 380 HalPropConfig config = mAllProperties.get(propId); 381 mPropertyHandlers.append(propId, service); 382 configsForService.add(config); 383 } 384 } 385 } else { 386 for (int prop : supportedProps) { 387 HalPropConfig config = mAllProperties.get(prop); 388 if (config == null) { 389 continue; 390 } 391 mPropertyHandlers.append(prop, service); 392 configsForService.add(config); 393 } 394 } 395 } 396 } 397 398 for (Map.Entry<HalServiceBase, ArrayList<HalPropConfig>> entry 399 : configsForAllServices.entrySet()) { 400 HalServiceBase service = entry.getKey(); 401 ArrayList<HalPropConfig> configsForService = entry.getValue(); 402 service.takeProperties(configsForService); 403 service.init(); 404 } 405 } 406 407 /** 408 * Releases all connected services (power management service, input service, etc). 409 */ 410 @Override release()411 public void release() { 412 ArraySet<Integer> subscribedProperties = new ArraySet<>(); 413 synchronized (mLock) { 414 // release in reverse order from init 415 for (int i = mAllServices.size() - 1; i >= 0; i--) { 416 mAllServices.get(i).release(); 417 } 418 for (int i = 0; i < mUpdateRateByPropIdAreadId.size(); i++) { 419 subscribedProperties.add(mUpdateRateByPropIdAreadId.keyAt(i).first); 420 } 421 mUpdateRateByPropIdAreadId.clear(); 422 mAllProperties.clear(); 423 } 424 for (int i = 0; i < subscribedProperties.size(); i++) { 425 try { 426 mSubscriptionClient.unsubscribe(subscribedProperties.valueAt(i)); 427 } catch (RemoteException | ServiceSpecificException e) { 428 // Ignore exceptions on shutdown path. 429 Slogf.w(CarLog.TAG_HAL, "Failed to unsubscribe", e); 430 } 431 } 432 // keep the looper thread as should be kept for the whole life cycle. 433 } 434 getDiagnosticHal()435 public DiagnosticHalService getDiagnosticHal() { 436 return mDiagnosticHal; 437 } 438 getPowerHal()439 public PowerHalService getPowerHal() { 440 return mPowerHal; 441 } 442 getPropertyHal()443 public PropertyHalService getPropertyHal() { 444 return mPropertyHal; 445 } 446 getInputHal()447 public InputHalService getInputHal() { 448 return mInputHal; 449 } 450 getUserHal()451 public UserHalService getUserHal() { 452 return mUserHal; 453 } 454 getVmsHal()455 public VmsHalService getVmsHal() { 456 return mVmsHal; 457 } 458 getClusterHal()459 public ClusterHalService getClusterHal() { 460 return mClusterHalService; 461 } 462 getEvsHal()463 public EvsHalService getEvsHal() { 464 return mEvsHal; 465 } 466 getTimeHalService()467 public TimeHalService getTimeHalService() { 468 return mTimeHalService; 469 } 470 getHalPropValueBuilder()471 public HalPropValueBuilder getHalPropValueBuilder() { 472 return mPropValueBuilder; 473 } 474 475 @GuardedBy("mLock") assertServiceOwnerLocked(HalServiceBase service, int property)476 private void assertServiceOwnerLocked(HalServiceBase service, int property) { 477 if (service != mPropertyHandlers.get(property)) { 478 throw new IllegalArgumentException(String.format( 479 "Property 0x%x is not owned by service: %s", property, service)); 480 } 481 } 482 483 /** 484 * Subscribes given properties with sampling rate defaults to 0 and no special flags provided. 485 * 486 * @throws IllegalArgumentException thrown if property is not supported by VHAL 487 * @see #subscribeProperty(HalServiceBase, int, float) 488 */ subscribeProperty(HalServiceBase service, int property)489 public void subscribeProperty(HalServiceBase service, int property) 490 throws IllegalArgumentException { 491 subscribeProperty(service, property, /* samplingRateHz= */ 0f); 492 } 493 494 /** 495 * Subscribe given property. Only Hal service owning the property can subscribe it. 496 * 497 * @param service HalService that owns this property 498 * @param property property id (VehicleProperty) 499 * @param samplingRateHz sampling rate in Hz for continuous properties 500 * @throws IllegalArgumentException thrown if property is not supported by VHAL 501 */ subscribeProperty(HalServiceBase service, int property, float samplingRateHz)502 public void subscribeProperty(HalServiceBase service, int property, float samplingRateHz) 503 throws IllegalArgumentException { 504 subscribeProperty(service, property, samplingRateHz, new int[0]); 505 } 506 507 /** 508 * Subscribe given property. Only Hal service owning the property can subscribe it. 509 * 510 * @param service HalService that owns this property 511 * @param property property id (VehicleProperty) 512 * @param samplingRateHz sampling rate in Hz for continuous properties 513 * @param areaIds The areaId that is being subscribed to, if empty subscribe to all areas 514 * @throws IllegalArgumentException thrown if property is not supported by VHAL 515 */ subscribeProperty(HalServiceBase service, int property, float samplingRateHz, int[] areaIds)516 public void subscribeProperty(HalServiceBase service, int property, float samplingRateHz, 517 int[] areaIds) { 518 if (DBG) { 519 Slogf.d(CarLog.TAG_HAL, "subscribeProperty, service, areaIds, SamplingRateHz:" 520 + toCarPropertyLog(property) + ", " + service + ", " 521 + CarServiceUtils.asList(areaIds) + ", " + samplingRateHz); 522 } 523 HalPropConfig config; 524 synchronized (mLock) { 525 config = mAllProperties.get(property); 526 } 527 528 if (config == null) { 529 throw new IllegalArgumentException( 530 String.format("subscribe error: config is null for property 0x%x", property)); 531 } else if (isPropertySubscribable(config)) { 532 if (areaIds.length == 0) { 533 areaIds = getAllAreaIdsFromPropertyId(config); 534 } 535 SubscribeOptions opts = new SubscribeOptions(); 536 opts.propId = property; 537 opts.sampleRate = samplingRateHz; 538 int[] filteredAreaIds = checkAlreadySubscribed(property, areaIds, samplingRateHz); 539 opts.areaIds = filteredAreaIds; 540 if (opts.areaIds.length == 0) { 541 Slogf.w(CarLog.TAG_HAL, "property: " + VehiclePropertyIds.toString(property) 542 + " is already subscribed at rate: " + samplingRateHz + " hz"); 543 return; 544 } 545 synchronized (mLock) { 546 assertServiceOwnerLocked(service, property); 547 for (int i = 0; i < filteredAreaIds.length; i++) { 548 mUpdateRateByPropIdAreadId.put(Pair.create(property, 549 filteredAreaIds[i]), samplingRateHz); 550 } 551 } 552 try { 553 mSubscriptionClient.subscribe(new SubscribeOptions[]{opts}); 554 } catch (RemoteException | ServiceSpecificException e) { 555 Slogf.w(CarLog.TAG_HAL, "Failed to subscribe to " + toCarPropertyLog(property), 556 e); 557 } 558 } else { 559 Slogf.w(CarLog.TAG_HAL, "Cannot subscribe to " + toCarPropertyLog(property)); 560 } 561 } 562 checkAlreadySubscribed(int property, int[] areaIds, float sampleRateHz)563 private int[] checkAlreadySubscribed(int property, int[] areaIds, float sampleRateHz) { 564 List<Integer> areaIdList = new ArrayList<>(); 565 synchronized (mLock) { 566 for (int i = 0; i < areaIds.length; i++) { 567 Pair<Integer, Integer> propertyAndAreadId = Pair.create(property, areaIds[i]); 568 Float savedSampleRateHz = mUpdateRateByPropIdAreadId.get(propertyAndAreadId); 569 if (savedSampleRateHz != null 570 && savedSampleRateHz - sampleRateHz < PRECISION_THRESHOLD) { 571 continue; 572 } 573 areaIdList.add(areaIds[i]); 574 } 575 } 576 return CarServiceUtils.toIntArray(areaIdList); 577 } 578 getAllAreaIdsFromPropertyId(HalPropConfig config)579 private int[] getAllAreaIdsFromPropertyId(HalPropConfig config) { 580 HalAreaConfig[] allAreaConfigs = config.getAreaConfigs(); 581 if (allAreaConfigs.length == 0) { 582 return new int[]{/* areaId= */ 0}; 583 } 584 int[] areaId = new int[allAreaConfigs.length]; 585 for (int i = 0; i < allAreaConfigs.length; i++) { 586 areaId[i] = allAreaConfigs[i].getAreaId(); 587 } 588 return areaId; 589 } 590 591 /** 592 * Unsubscribes from receiving notifications for the property and HAL services passed 593 * as parameters. 594 */ unsubscribeProperty(HalServiceBase service, int property)595 public void unsubscribeProperty(HalServiceBase service, int property) { 596 if (DBG) { 597 Slogf.i(CarLog.TAG_HAL, "unsubscribeProperty, service:" + service 598 + ", " + toCarPropertyLog(property)); 599 } 600 HalPropConfig config; 601 synchronized (mLock) { 602 config = mAllProperties.get(property); 603 } 604 605 if (config == null) { 606 Slogf.w(CarLog.TAG_HAL, "unsubscribeProperty " + toCarPropertyLog(property) 607 + " does not exist"); 608 } else if (isPropertySubscribable(config)) { 609 synchronized (mLock) { 610 assertServiceOwnerLocked(service, property); 611 int[] areaIds = getAllAreaIdsFromPropertyId(config); 612 for (int i = 0; i < areaIds.length; i++) { 613 mUpdateRateByPropIdAreadId.remove(Pair.create(property, areaIds[i])); 614 } 615 } 616 try { 617 mSubscriptionClient.unsubscribe(property); 618 } catch (RemoteException | ServiceSpecificException e) { 619 Slogf.w(CarLog.TAG_SERVICE, "Failed to unsubscribe: " 620 + toCarPropertyLog(property), e); 621 } 622 } else { 623 Slogf.w(CarLog.TAG_HAL, "Cannot unsubscribe " + toCarPropertyLog(property)); 624 } 625 } 626 627 /** 628 * Indicates if the property passed as parameter is supported. 629 */ isPropertySupported(int propertyId)630 public boolean isPropertySupported(int propertyId) { 631 synchronized (mLock) { 632 return mAllProperties.contains(propertyId); 633 } 634 } 635 636 /** 637 * Gets given property with retries. 638 * 639 * <p>If getting the property fails after all retries, it will throw 640 * {@code IllegalStateException}. If the property is not supported, it will simply return 641 * {@code null}. 642 */ 643 @Nullable getIfSupportedOrFail(int propertyId, int maxRetries)644 public HalPropValue getIfSupportedOrFail(int propertyId, int maxRetries) { 645 if (!isPropertySupported(propertyId)) { 646 return null; 647 } 648 try { 649 return getValueWithRetry(mPropValueBuilder.build(propertyId, NO_AREA), maxRetries); 650 } catch (Exception e) { 651 throw new IllegalStateException(e); 652 } 653 } 654 655 /** 656 * This works similar to {@link #getIfSupportedOrFail(int, int)} except that this can be called 657 * before {@code init()} is called. 658 * 659 * <p>This call will check if requested vhal property is supported by querying directly to vhal 660 * and can have worse performance. Use this only for accessing vhal properties before 661 * {@code ICarImpl.init()} phase. 662 */ 663 @Nullable getIfSupportedOrFailForEarlyStage(int propertyId, int maxRetries)664 public HalPropValue getIfSupportedOrFailForEarlyStage(int propertyId, int maxRetries) { 665 fetchAllPropConfigs(); 666 return getIfSupportedOrFail(propertyId, maxRetries); 667 } 668 669 /** 670 * Returns the property's {@link HalPropValue} for the property id passed as parameter and 671 * not specified area. 672 * 673 * @throws IllegalArgumentException if argument is invalid 674 * @throws ServiceSpecificException if VHAL returns error 675 */ get(int propertyId)676 public HalPropValue get(int propertyId) 677 throws IllegalArgumentException, ServiceSpecificException { 678 return get(propertyId, NO_AREA); 679 } 680 681 /** 682 * Returns the property's {@link HalPropValue} for the property id and area id passed as 683 * parameters. 684 * 685 * @throws IllegalArgumentException if argument is invalid 686 * @throws ServiceSpecificException if VHAL returns error 687 */ get(int propertyId, int areaId)688 public HalPropValue get(int propertyId, int areaId) 689 throws IllegalArgumentException, ServiceSpecificException { 690 if (DBG) { 691 Slogf.i(CarLog.TAG_HAL, "get, " + toCarPropertyLog(propertyId) 692 + toCarAreaLog(areaId)); 693 } 694 return getValueWithRetry(mPropValueBuilder.build(propertyId, areaId)); 695 } 696 697 /** 698 * Returns the property object value for the class and property id passed as parameter and 699 * no area specified. 700 * 701 * @throws IllegalArgumentException if argument is invalid 702 * @throws ServiceSpecificException if VHAL returns error 703 */ get(Class clazz, int propertyId)704 public <T> T get(Class clazz, int propertyId) 705 throws IllegalArgumentException, ServiceSpecificException { 706 return get(clazz, propertyId, NO_AREA); 707 } 708 709 /** 710 * Returns the property object value for the class, property id, and area id passed as 711 * parameter. 712 * 713 * @throws IllegalArgumentException if argument is invalid 714 * @throws ServiceSpecificException if VHAL returns error 715 */ get(Class clazz, int propertyId, int areaId)716 public <T> T get(Class clazz, int propertyId, int areaId) 717 throws IllegalArgumentException, ServiceSpecificException { 718 return get(clazz, mPropValueBuilder.build(propertyId, areaId)); 719 } 720 721 /** 722 * Returns the property object value for the class and requested property value passed as 723 * parameter. 724 * 725 * @throws IllegalArgumentException if argument is invalid 726 * @throws ServiceSpecificException if VHAL returns error 727 */ 728 @SuppressWarnings("unchecked") get(Class clazz, HalPropValue requestedPropValue)729 public <T> T get(Class clazz, HalPropValue requestedPropValue) 730 throws IllegalArgumentException, ServiceSpecificException { 731 HalPropValue propValue; 732 propValue = getValueWithRetry(requestedPropValue); 733 734 if (clazz == Long.class || clazz == long.class) { 735 Long value = propValue.getInt64Value(0); 736 return (T) value; 737 } else if (clazz == Integer.class || clazz == int.class) { 738 Integer value = propValue.getInt32Value(0); 739 return (T) value; 740 } else if (clazz == Boolean.class || clazz == boolean.class) { 741 Boolean value = Boolean.valueOf(propValue.getInt32Value(0) == 1); 742 return (T) value; 743 } else if (clazz == Float.class || clazz == float.class) { 744 Float value = propValue.getFloatValue(0); 745 return (T) value; 746 } else if (clazz == Long[].class) { 747 int size = propValue.getInt64ValuesSize(); 748 Long[] longArray = new Long[size]; 749 for (int i = 0; i < size; i++) { 750 longArray[i] = propValue.getInt64Value(i); 751 } 752 return (T) longArray; 753 } else if (clazz == Integer[].class) { 754 int size = propValue.getInt32ValuesSize(); 755 Integer[] intArray = new Integer[size]; 756 for (int i = 0; i < size; i++) { 757 intArray[i] = propValue.getInt32Value(i); 758 } 759 return (T) intArray; 760 } else if (clazz == Float[].class) { 761 int size = propValue.getFloatValuesSize(); 762 Float[] floatArray = new Float[size]; 763 for (int i = 0; i < size; i++) { 764 floatArray[i] = propValue.getFloatValue(i); 765 } 766 return (T) floatArray; 767 } else if (clazz == long[].class) { 768 int size = propValue.getInt64ValuesSize(); 769 long[] longArray = new long[size]; 770 for (int i = 0; i < size; i++) { 771 longArray[i] = propValue.getInt64Value(i); 772 } 773 return (T) longArray; 774 } else if (clazz == int[].class) { 775 int size = propValue.getInt32ValuesSize(); 776 int[] intArray = new int[size]; 777 for (int i = 0; i < size; i++) { 778 intArray[i] = propValue.getInt32Value(i); 779 } 780 return (T) intArray; 781 } else if (clazz == float[].class) { 782 int size = propValue.getFloatValuesSize(); 783 float[] floatArray = new float[size]; 784 for (int i = 0; i < size; i++) { 785 floatArray[i] = propValue.getFloatValue(i); 786 } 787 return (T) floatArray; 788 } else if (clazz == byte[].class) { 789 return (T) propValue.getByteArray(); 790 } else if (clazz == String.class) { 791 return (T) propValue.getStringValue(); 792 } else { 793 throw new IllegalArgumentException("Unexpected type: " + clazz); 794 } 795 } 796 797 /** 798 * Returns the vehicle's {@link HalPropValue} for the requested property value passed 799 * as parameter. 800 * 801 * @throws IllegalArgumentException if argument is invalid 802 * @throws ServiceSpecificException if VHAL returns error 803 */ get(HalPropValue requestedPropValue)804 public HalPropValue get(HalPropValue requestedPropValue) 805 throws IllegalArgumentException, ServiceSpecificException { 806 return getValueWithRetry(requestedPropValue); 807 } 808 809 /** 810 * Set property. 811 * 812 * @throws IllegalArgumentException if argument is invalid 813 * @throws ServiceSpecificException if VHAL returns error 814 */ set(HalPropValue propValue)815 public void set(HalPropValue propValue) 816 throws IllegalArgumentException, ServiceSpecificException { 817 setValueWithRetry(propValue); 818 } 819 820 @CheckResult set(int propId)821 HalPropValueSetter set(int propId) { 822 return set(propId, NO_AREA); 823 } 824 825 @CheckResult set(int propId, int areaId)826 HalPropValueSetter set(int propId, int areaId) { 827 return new HalPropValueSetter(propId, areaId); 828 } 829 isPropertySubscribable(HalPropConfig config)830 static boolean isPropertySubscribable(HalPropConfig config) { 831 return (config.getAccess() & VehiclePropertyAccess.READ) != 0 832 && (config.getChangeMode() != VehiclePropertyChangeMode.STATIC); 833 } 834 835 /** 836 * Sets a property passed from the shell command. 837 * 838 * @param property Property ID in hex or decimal. 839 * @param areaId Area ID 840 * @param data Comma-separated value. 841 */ setPropertyFromCommand(int property, int areaId, String data, IndentingPrintWriter writer)842 public void setPropertyFromCommand(int property, int areaId, String data, 843 IndentingPrintWriter writer) throws IllegalArgumentException, ServiceSpecificException { 844 long timestamp = SystemClock.elapsedRealtimeNanos(); 845 HalPropValue v = createPropValueForInjecting(mPropValueBuilder, property, areaId, 846 List.of(data.split(DATA_DELIMITER)), timestamp); 847 if (v == null) { 848 throw new IllegalArgumentException("Unsupported property type: property=" + property 849 + ", areaId=" + areaId); 850 } 851 set(v); 852 } 853 854 private final ArraySet<HalServiceBase> mServicesToDispatch = new ArraySet<>(); 855 856 @Override onPropertyEvent(ArrayList<HalPropValue> propValues)857 public void onPropertyEvent(ArrayList<HalPropValue> propValues) { 858 mHandler.post(() -> handleOnPropertyEvent(propValues)); 859 } 860 861 @Override onPropertySetError(ArrayList<VehiclePropError> errors)862 public void onPropertySetError(ArrayList<VehiclePropError> errors) { 863 mHandler.post(() -> handleOnPropertySetError(errors)); 864 } 865 866 @Override 867 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)868 public void dump(IndentingPrintWriter writer) { 869 synchronized (mLock) { 870 writer.println("**dump HAL services**"); 871 for (int i = 0; i < mAllServices.size(); i++) { 872 mAllServices.get(i).dump(writer); 873 } 874 // Dump all VHAL property configure. 875 dumpPropertyConfigs(writer, -1); 876 writer.printf("**All Events, now ns:%d**\n", 877 SystemClock.elapsedRealtimeNanos()); 878 for (int i = 0; i < mEventLog.size(); i++) { 879 VehiclePropertyEventInfo info = mEventLog.valueAt(i); 880 writer.printf("event count:%d, lastEvent: ", info.mEventCount); 881 dumpPropValue(writer, info.mLastEvent); 882 } 883 writer.println("**Property handlers**"); 884 for (int i = 0; i < mPropertyHandlers.size(); i++) { 885 int propId = mPropertyHandlers.keyAt(i); 886 HalServiceBase service = mPropertyHandlers.valueAt(i); 887 writer.printf("Property Id: %d // 0x%x name: %s, service: %s\n", propId, propId, 888 VehiclePropertyIds.toString(propId), service); 889 } 890 } 891 } 892 893 /** 894 * Dumps or debug VHAL. 895 */ 896 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpVhal(ParcelFileDescriptor fd, List<String> options)897 public void dumpVhal(ParcelFileDescriptor fd, List<String> options) throws RemoteException { 898 mVehicleStub.dump(fd.getFileDescriptor(), options); 899 } 900 901 /** 902 * Dumps the list of HALs. 903 */ dumpListHals(PrintWriter writer)904 public void dumpListHals(PrintWriter writer) { 905 synchronized (mLock) { 906 for (int i = 0; i < mAllServices.size(); i++) { 907 writer.println(mAllServices.get(i).getClass().getName()); 908 } 909 } 910 } 911 912 /** 913 * Dumps the given HALs. 914 */ dumpSpecificHals(PrintWriter writer, String... halNames)915 public void dumpSpecificHals(PrintWriter writer, String... halNames) { 916 synchronized (mLock) { 917 Map<String, HalServiceBase> byName = mAllServices.stream() 918 .collect(Collectors.toMap(s -> s.getClass().getSimpleName(), s -> s)); 919 for (String halName : halNames) { 920 HalServiceBase service = byName.get(halName); 921 if (service == null) { 922 writer.printf("No HAL named %s. Valid options are: %s\n", 923 halName, byName.keySet()); 924 continue; 925 } 926 service.dump(writer); 927 } 928 } 929 } 930 931 /** 932 * Dumps vehicle property values. 933 * 934 * @param propId property id, dump all properties' value if it is empty string 935 * @param areaId areaId of the property, dump the property for all areaIds in the config 936 * if it is empty string 937 */ dumpPropertyValueByCommand(PrintWriter writer, int propId, int areaId)938 public void dumpPropertyValueByCommand(PrintWriter writer, int propId, int areaId) { 939 if (propId == -1) { 940 writer.println("**All property values**"); 941 synchronized (mLock) { 942 for (int i = 0; i < mAllProperties.size(); i++) { 943 HalPropConfig config = mAllProperties.valueAt(i); 944 dumpPropertyValueByConfig(writer, config); 945 } 946 } 947 } else if (areaId == -1) { 948 synchronized (mLock) { 949 HalPropConfig config = mAllProperties.get(propId); 950 if (config == null) { 951 writer.print("Property "); 952 dumpPropHelper(writer, propId); 953 writer.print(" not supported by HAL\n"); 954 return; 955 } 956 dumpPropertyValueByConfig(writer, config); 957 } 958 } else { 959 try { 960 HalPropValue value = get(propId, areaId); 961 dumpPropValue(writer, value); 962 } catch (RuntimeException e) { 963 writer.printf("Can not get property value for property: %d // 0x%x " 964 + "in areaId: %d // 0x%x.\n", propId, propId, areaId, areaId); 965 } 966 } 967 } 968 969 /** 970 * Gets all property configs from VHAL. 971 */ getAllPropConfigs()972 public HalPropConfig[] getAllPropConfigs() throws RemoteException, ServiceSpecificException { 973 return mVehicleStub.getAllPropConfigs(); 974 } 975 976 /** 977 * Gets the property config for a property, returns {@code null} if not supported. 978 */ getPropConfig(int propId)979 public @Nullable HalPropConfig getPropConfig(int propId) { 980 synchronized (mLock) { 981 return mAllProperties.get(propId); 982 } 983 } 984 985 /** 986 * Checks whether we are connected to AIDL VHAL: {@code true} or HIDL VHAL: {@code false}. 987 */ isAidlVhal()988 public boolean isAidlVhal() { 989 return mVehicleStub.isAidlVhal(); 990 } 991 992 /** 993 * Checks if fake VHAL mode is enabled. 994 * 995 * @return {@code true} if car service is connected to FakeVehicleStub. 996 */ isFakeModeEnabled()997 public boolean isFakeModeEnabled() { 998 return mVehicleStub.isFakeModeEnabled(); 999 } 1000 dumpPropHelper(PrintWriter pw, int propId)1001 private static void dumpPropHelper(PrintWriter pw, int propId) { 1002 pw.printf("Id: %d // 0x%x, name: %s ", propId, propId, VehiclePropertyIds.toString(propId)); 1003 } 1004 dumpPropertyValueByConfig(PrintWriter writer, HalPropConfig config)1005 private void dumpPropertyValueByConfig(PrintWriter writer, HalPropConfig config) { 1006 int propId = config.getPropId(); 1007 HalAreaConfig[] areaConfigs = config.getAreaConfigs(); 1008 if (areaConfigs == null || areaConfigs.length == 0) { 1009 try { 1010 HalPropValue value = get(config.getPropId()); 1011 dumpPropValue(writer, value); 1012 } catch (RuntimeException e) { 1013 writer.printf("Can not get property value for property: %d // 0x%x," 1014 + " areaId: 0 \n", propId, propId); 1015 } 1016 } else { 1017 for (HalAreaConfig areaConfig : areaConfigs) { 1018 int areaId = areaConfig.getAreaId(); 1019 try { 1020 HalPropValue value = get(propId, areaId); 1021 dumpPropValue(writer, value); 1022 } catch (RuntimeException e) { 1023 writer.printf("Can not get property value for property: %d // 0x%x " 1024 + "in areaId: %d // 0x%x\n", propId, propId, areaId, areaId); 1025 } 1026 } 1027 } 1028 } 1029 1030 /** 1031 * Dump VHAL property configs. 1032 * Dump all properties if propid param is empty. 1033 * 1034 * @param propId the property ID 1035 */ dumpPropertyConfigs(PrintWriter writer, int propId)1036 public void dumpPropertyConfigs(PrintWriter writer, int propId) { 1037 HalPropConfig[] configs; 1038 synchronized (mLock) { 1039 configs = new HalPropConfig[mAllProperties.size()]; 1040 for (int i = 0; i < mAllProperties.size(); i++) { 1041 configs[i] = mAllProperties.valueAt(i); 1042 } 1043 } 1044 1045 if (propId == -1) { 1046 writer.println("**All properties**"); 1047 for (HalPropConfig config : configs) { 1048 dumpPropertyConfigsHelp(writer, config); 1049 } 1050 return; 1051 } 1052 for (HalPropConfig config : configs) { 1053 if (config.getPropId() == propId) { 1054 dumpPropertyConfigsHelp(writer, config); 1055 return; 1056 } 1057 } 1058 1059 } 1060 1061 /** Dumps VehiclePropertyConfigs */ dumpPropertyConfigsHelp(PrintWriter writer, HalPropConfig config)1062 private static void dumpPropertyConfigsHelp(PrintWriter writer, HalPropConfig config) { 1063 int propId = config.getPropId(); 1064 writer.printf("Property:0x%x, Property name:%s, access:0x%x, changeMode:0x%x, " 1065 + "config:%s, fs min:%f, fs max:%f\n", 1066 propId, VehiclePropertyIds.toString(propId), config.getAccess(), 1067 config.getChangeMode(), Arrays.toString(config.getConfigArray()), 1068 config.getMinSampleRate(), config.getMaxSampleRate()); 1069 if (config.getAreaConfigs() == null) { 1070 return; 1071 } 1072 for (HalAreaConfig area : config.getAreaConfigs()) { 1073 writer.printf("\tareaId:0x%x, f min:%f, f max:%f, i min:%d, i max:%d," 1074 + " i64 min:%d, i64 max:%d\n", 1075 area.getAreaId(), area.getMinFloatValue(), area.getMaxFloatValue(), 1076 area.getMinInt32Value(), area.getMaxInt32Value(), area.getMinInt64Value(), 1077 area.getMaxInt64Value()); 1078 } 1079 } 1080 1081 /** 1082 * Inject a VHAL event 1083 * 1084 * @param property the Vehicle property Id as defined in the HAL 1085 * @param zone the zone that this event services 1086 * @param value the data value of the event 1087 * @param delayTime add a certain duration to event timestamp 1088 */ injectVhalEvent(int property, int zone, String value, int delayTime)1089 public void injectVhalEvent(int property, int zone, String value, int delayTime) 1090 throws NumberFormatException { 1091 long timestamp = SystemClock.elapsedRealtimeNanos() + TimeUnit.SECONDS.toNanos(delayTime); 1092 HalPropValue v = createPropValueForInjecting(mPropValueBuilder, property, zone, 1093 Arrays.asList(value.split(DATA_DELIMITER)), timestamp); 1094 if (v == null) { 1095 return; 1096 } 1097 mHandler.post(() -> handleOnPropertyEvent(Lists.newArrayList(v))); 1098 } 1099 1100 /** 1101 * Injects continuous VHAL events. 1102 * 1103 * @param property the Vehicle property Id as defined in the HAL 1104 * @param zone the zone that this event services 1105 * @param value the data value of the event 1106 * @param sampleRate the sample rate for events in Hz 1107 * @param timeDurationInSec the duration for injecting events in seconds 1108 */ injectContinuousVhalEvent(int property, int zone, String value, float sampleRate, long timeDurationInSec)1109 public void injectContinuousVhalEvent(int property, int zone, String value, 1110 float sampleRate, long timeDurationInSec) { 1111 1112 HalPropValue v = createPropValueForInjecting(mPropValueBuilder, property, zone, 1113 new ArrayList<>(Arrays.asList(value.split(DATA_DELIMITER))), 0); 1114 if (v == null) { 1115 return; 1116 } 1117 // rate in Hz 1118 if (sampleRate <= 0) { 1119 Slogf.e(CarLog.TAG_HAL, "Inject events at an invalid sample rate: " + sampleRate); 1120 return; 1121 } 1122 long period = (long) (1000 / sampleRate); 1123 long stopTime = timeDurationInSec * 1000 + SystemClock.elapsedRealtime(); 1124 Timer timer = new Timer(); 1125 timer.schedule(new TimerTask() { 1126 @Override 1127 public void run() { 1128 if (stopTime < SystemClock.elapsedRealtime()) { 1129 timer.cancel(); 1130 timer.purge(); 1131 } else { 1132 // Avoid the fake events be covered by real Event 1133 long timestamp = SystemClock.elapsedRealtimeNanos() 1134 + TimeUnit.SECONDS.toNanos(timeDurationInSec); 1135 HalPropValue v = createPropValueForInjecting(mPropValueBuilder, property, zone, 1136 new ArrayList<>(Arrays.asList(value.split(DATA_DELIMITER))), timestamp); 1137 mHandler.post(() -> handleOnPropertyEvent(Lists.newArrayList(v))); 1138 } 1139 } 1140 }, /* delay= */0, period); 1141 } 1142 1143 // Returns null if the property type is unsupported. 1144 @Nullable createPropValueForInjecting(HalPropValueBuilder builder, int propId, int zoneId, List<String> dataList, long timestamp)1145 private static HalPropValue createPropValueForInjecting(HalPropValueBuilder builder, 1146 int propId, int zoneId, List<String> dataList, long timestamp) { 1147 int propertyType = propId & VehiclePropertyType.MASK; 1148 // Values can be comma separated list 1149 switch (propertyType) { 1150 case VehiclePropertyType.BOOLEAN: 1151 boolean boolValue = Boolean.parseBoolean(dataList.get(0)); 1152 return builder.build(propId, zoneId, timestamp, VehiclePropertyStatus.AVAILABLE, 1153 boolValue ? 1 : 0); 1154 case VehiclePropertyType.INT64: 1155 case VehiclePropertyType.INT64_VEC: 1156 long[] longValues = new long[dataList.size()]; 1157 for (int i = 0; i < dataList.size(); i++) { 1158 longValues[i] = Long.decode(dataList.get(i)); 1159 } 1160 return builder.build(propId, zoneId, timestamp, VehiclePropertyStatus.AVAILABLE, 1161 longValues); 1162 case VehiclePropertyType.INT32: 1163 case VehiclePropertyType.INT32_VEC: 1164 int[] intValues = new int[dataList.size()]; 1165 for (int i = 0; i < dataList.size(); i++) { 1166 intValues[i] = Integer.decode(dataList.get(i)); 1167 } 1168 return builder.build(propId, zoneId, timestamp, VehiclePropertyStatus.AVAILABLE, 1169 intValues); 1170 case VehiclePropertyType.FLOAT: 1171 case VehiclePropertyType.FLOAT_VEC: 1172 float[] floatValues = new float[dataList.size()]; 1173 for (int i = 0; i < dataList.size(); i++) { 1174 floatValues[i] = Float.parseFloat(dataList.get(i)); 1175 } 1176 return builder.build(propId, zoneId, timestamp, VehiclePropertyStatus.AVAILABLE, 1177 floatValues); 1178 default: 1179 Slogf.e(CarLog.TAG_HAL, "Property type unsupported:" + propertyType); 1180 return null; 1181 } 1182 } 1183 1184 private static class VehiclePropertyEventInfo { 1185 private int mEventCount; 1186 private HalPropValue mLastEvent; 1187 VehiclePropertyEventInfo(HalPropValue event)1188 private VehiclePropertyEventInfo(HalPropValue event) { 1189 mEventCount = 1; 1190 mLastEvent = event; 1191 } 1192 addNewEvent(HalPropValue event)1193 private void addNewEvent(HalPropValue event) { 1194 mEventCount++; 1195 mLastEvent = event; 1196 } 1197 } 1198 1199 final class HalPropValueSetter { 1200 final int mPropId; 1201 final int mAreaId; 1202 HalPropValueSetter(int propId, int areaId)1203 private HalPropValueSetter(int propId, int areaId) { 1204 mPropId = propId; 1205 mAreaId = areaId; 1206 } 1207 1208 /** 1209 * Set the property to the given value. 1210 * 1211 * @throws IllegalArgumentException if argument is invalid 1212 * @throws ServiceSpecificException if VHAL returns error 1213 */ to(boolean value)1214 void to(boolean value) throws IllegalArgumentException, ServiceSpecificException { 1215 to(value ? 1 : 0); 1216 } 1217 1218 /** 1219 * Set the property to the given value. 1220 * 1221 * @throws IllegalArgumentException if argument is invalid 1222 * @throws ServiceSpecificException if VHAL returns error 1223 */ to(int value)1224 void to(int value) throws IllegalArgumentException, ServiceSpecificException { 1225 HalPropValue propValue = mPropValueBuilder.build(mPropId, mAreaId, value); 1226 submit(propValue); 1227 } 1228 1229 /** 1230 * Set the property to the given values. 1231 * 1232 * @throws IllegalArgumentException if argument is invalid 1233 * @throws ServiceSpecificException if VHAL returns error 1234 */ to(int[] values)1235 void to(int[] values) throws IllegalArgumentException, ServiceSpecificException { 1236 HalPropValue propValue = mPropValueBuilder.build(mPropId, mAreaId, values); 1237 submit(propValue); 1238 } 1239 1240 /** 1241 * Set the property to the given values. 1242 * 1243 * @throws IllegalArgumentException if argument is invalid 1244 * @throws ServiceSpecificException if VHAL returns error 1245 */ to(Collection<Integer> values)1246 void to(Collection<Integer> values) 1247 throws IllegalArgumentException, ServiceSpecificException { 1248 int[] intValues = new int[values.size()]; 1249 int i = 0; 1250 for (int value : values) { 1251 intValues[i] = value; 1252 i++; 1253 } 1254 HalPropValue propValue = mPropValueBuilder.build(mPropId, mAreaId, intValues); 1255 submit(propValue); 1256 } 1257 submit(HalPropValue propValue)1258 void submit(HalPropValue propValue) 1259 throws IllegalArgumentException, ServiceSpecificException { 1260 if (DBG) { 1261 Slogf.i(CarLog.TAG_HAL, "set, " + toCarPropertyLog(mPropId) 1262 + toCarAreaLog(mAreaId)); 1263 } 1264 setValueWithRetry(propValue); 1265 } 1266 } 1267 dumpPropValue(PrintWriter writer, HalPropValue value)1268 private static void dumpPropValue(PrintWriter writer, HalPropValue value) { 1269 String bytesString = ""; 1270 byte[] byteValues = value.getByteArray(); 1271 if (byteValues.length > MAX_BYTE_SIZE) { 1272 byte[] bytes = Arrays.copyOf(byteValues, MAX_BYTE_SIZE); 1273 bytesString = Arrays.toString(bytes); 1274 } else { 1275 bytesString = Arrays.toString(byteValues); 1276 } 1277 1278 writer.printf("Property:0x%x, status: %d, timestamp: %d, zone: 0x%x, " 1279 + "floatValues: %s, int32Values: %s, int64Values: %s, bytes: %s, string: " 1280 + "%s\n", 1281 value.getPropId(), value.getStatus(), value.getTimestamp(), value.getAreaId(), 1282 value.dumpFloatValues(), value.dumpInt32Values(), value.dumpInt64Values(), 1283 bytesString, value.getStringValue()); 1284 } 1285 toCarPropertyLog(int propId)1286 private static String toCarPropertyLog(int propId) { 1287 return String.format("property Id: %d // 0x%x, property name: %s ", propId, propId, 1288 VehiclePropertyIds.toString(propId)); 1289 } 1290 1291 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) toCarAreaLog(int areaId)1292 private static String toCarAreaLog(int areaId) { 1293 return String.format("areaId: %d // 0x%x", areaId, areaId); 1294 } 1295 1296 interface RetriableAction { run(HalPropValue requestValue)1297 @Nullable HalPropValue run(HalPropValue requestValue) 1298 throws ServiceSpecificException, RemoteException; 1299 } 1300 invokeRetriable(RetriableAction action, String operation, HalPropValue requestValue, long maxDurationForRetryMs, long sleepBetweenRetryMs, int maxRetries)1301 private static HalPropValue invokeRetriable(RetriableAction action, 1302 String operation, HalPropValue requestValue, long maxDurationForRetryMs, 1303 long sleepBetweenRetryMs, int maxRetries) 1304 throws ServiceSpecificException, IllegalArgumentException { 1305 Retrier retrier = new Retrier(action, operation, requestValue, maxDurationForRetryMs, 1306 sleepBetweenRetryMs, maxRetries); 1307 return retrier.invokeAction(); 1308 } 1309 1310 private static final class Retrier { 1311 private final RetriableAction mAction; 1312 private final String mOperation; 1313 private final HalPropValue mRequestValue; 1314 private final long mMaxDurationForRetryMs; 1315 private final long mSleepBetweenRetryMs; 1316 private final int mMaxRetries; 1317 private final long mStartTime; 1318 private int mRetryCount = 0; 1319 Retrier(RetriableAction action, String operation, HalPropValue requestValue, long maxDurationForRetryMs, long sleepBetweenRetryMs, int maxRetries)1320 Retrier(RetriableAction action, 1321 String operation, HalPropValue requestValue, long maxDurationForRetryMs, 1322 long sleepBetweenRetryMs, int maxRetries) { 1323 mAction = action; 1324 mOperation = operation; 1325 mRequestValue = requestValue; 1326 mMaxDurationForRetryMs = maxDurationForRetryMs; 1327 mSleepBetweenRetryMs = sleepBetweenRetryMs; 1328 mMaxRetries = maxRetries; 1329 mStartTime = uptimeMillis(); 1330 } 1331 invokeAction()1332 HalPropValue invokeAction() 1333 throws ServiceSpecificException, IllegalArgumentException { 1334 mRetryCount++; 1335 1336 try { 1337 return mAction.run(mRequestValue); 1338 } catch (ServiceSpecificException e) { 1339 switch (e.errorCode) { 1340 case StatusCode.INVALID_ARG: 1341 throw new IllegalArgumentException(errorMessage(mOperation, mRequestValue, 1342 e.toString())); 1343 case StatusCode.TRY_AGAIN: 1344 return sleepAndTryAgain(e); 1345 default: 1346 throw e; 1347 } 1348 } catch (RemoteException e) { 1349 return sleepAndTryAgain(e); 1350 } 1351 } 1352 sleepAndTryAgain(Exception e)1353 private HalPropValue sleepAndTryAgain(Exception e) 1354 throws ServiceSpecificException, IllegalArgumentException { 1355 Slogf.d(CarLog.TAG_HAL, "trying the request: " 1356 + toCarPropertyLog(mRequestValue.getPropId()) + ", " 1357 + toCarAreaLog(mRequestValue.getAreaId()) + " again..."); 1358 try { 1359 Thread.sleep(mSleepBetweenRetryMs); 1360 } catch (InterruptedException interruptedException) { 1361 Thread.currentThread().interrupt(); 1362 Slogf.w(CarLog.TAG_HAL, "Thread was interrupted while waiting for vehicle HAL.", 1363 interruptedException); 1364 throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR, 1365 errorMessage(mOperation, mRequestValue, interruptedException.toString())); 1366 } 1367 1368 if (mMaxRetries != 0) { 1369 // If mMaxRetries is specified, check the retry count. 1370 if (mMaxRetries == mRetryCount) { 1371 throw new ServiceSpecificException(StatusCode.TRY_AGAIN, 1372 errorMessage(mOperation, mRequestValue, 1373 "cannot get property after " + mRetryCount + " retires, " 1374 + "last exception: " + e)); 1375 } 1376 } else if ((uptimeMillis() - mStartTime) >= mMaxDurationForRetryMs) { 1377 // Otherwise, check whether we have reached timeout. 1378 throw new ServiceSpecificException(StatusCode.TRY_AGAIN, 1379 errorMessage(mOperation, mRequestValue, 1380 "cannot get property within " + mMaxDurationForRetryMs 1381 + "ms, last exception: " + e)); 1382 } 1383 return invokeAction(); 1384 } 1385 } 1386 1387 1388 /** 1389 * Queries HalPropValue with list of GetVehicleHalRequest objects. 1390 * 1391 * <p>This method gets the HalPropValue using async methods. 1392 */ getAsync(List<VehicleStub.AsyncGetSetRequest> getVehicleStubAsyncRequests, VehicleStub.VehicleStubCallbackInterface getVehicleStubAsyncCallback)1393 public void getAsync(List<VehicleStub.AsyncGetSetRequest> getVehicleStubAsyncRequests, 1394 VehicleStub.VehicleStubCallbackInterface getVehicleStubAsyncCallback) { 1395 mVehicleStub.getAsync(getVehicleStubAsyncRequests, getVehicleStubAsyncCallback); 1396 } 1397 1398 /** 1399 * Sets vehicle property value asynchronously. 1400 */ setAsync(List<VehicleStub.AsyncGetSetRequest> setVehicleStubAsyncRequests, VehicleStub.VehicleStubCallbackInterface setVehicleStubAsyncCallback)1401 public void setAsync(List<VehicleStub.AsyncGetSetRequest> setVehicleStubAsyncRequests, 1402 VehicleStub.VehicleStubCallbackInterface setVehicleStubAsyncCallback) { 1403 mVehicleStub.setAsync(setVehicleStubAsyncRequests, setVehicleStubAsyncCallback); 1404 } 1405 1406 /** 1407 * Cancels all the on-going async requests with the given request IDs. 1408 */ cancelRequests(List<Integer> vehicleStubRequestIds)1409 public void cancelRequests(List<Integer> vehicleStubRequestIds) { 1410 mVehicleStub.cancelRequests(vehicleStubRequestIds); 1411 } 1412 } 1413