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.hal.property.HalPropertyDebugUtils.toAccessString; 22 import static com.android.car.hal.property.HalPropertyDebugUtils.toAreaIdString; 23 import static com.android.car.hal.property.HalPropertyDebugUtils.toAreaTypeString; 24 import static com.android.car.hal.property.HalPropertyDebugUtils.toChangeModeString; 25 import static com.android.car.hal.property.HalPropertyDebugUtils.toGroupString; 26 import static com.android.car.hal.property.HalPropertyDebugUtils.toPropertyIdString; 27 import static com.android.car.hal.property.HalPropertyDebugUtils.toValueTypeString; 28 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 29 30 import android.annotation.CheckResult; 31 import android.annotation.Nullable; 32 import android.car.VehiclePropertyIds; 33 import android.car.builtin.os.TraceHelper; 34 import android.car.builtin.util.Slogf; 35 import android.car.feature.FeatureFlags; 36 import android.car.feature.FeatureFlagsImpl; 37 import android.content.Context; 38 import android.hardware.automotive.vehicle.StatusCode; 39 import android.hardware.automotive.vehicle.SubscribeOptions; 40 import android.hardware.automotive.vehicle.VehiclePropError; 41 import android.hardware.automotive.vehicle.VehicleProperty; 42 import android.hardware.automotive.vehicle.VehiclePropertyAccess; 43 import android.hardware.automotive.vehicle.VehiclePropertyChangeMode; 44 import android.hardware.automotive.vehicle.VehiclePropertyStatus; 45 import android.hardware.automotive.vehicle.VehiclePropertyType; 46 import android.os.Handler; 47 import android.os.HandlerThread; 48 import android.os.ParcelFileDescriptor; 49 import android.os.RemoteException; 50 import android.os.ServiceSpecificException; 51 import android.os.SystemClock; 52 import android.os.Trace; 53 import android.util.ArrayMap; 54 import android.util.ArraySet; 55 import android.util.Log; 56 import android.util.SparseArray; 57 58 import com.android.car.CarLog; 59 import com.android.car.CarServiceUtils; 60 import com.android.car.CarSystemService; 61 import com.android.car.VehicleStub; 62 import com.android.car.VehicleStub.SubscriptionClient; 63 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 64 import com.android.car.internal.util.IndentingPrintWriter; 65 import com.android.car.internal.util.Lists; 66 import com.android.car.internal.util.PairSparseArray; 67 import com.android.internal.annotations.GuardedBy; 68 import com.android.internal.annotations.VisibleForTesting; 69 70 import java.io.PrintWriter; 71 import java.util.ArrayList; 72 import java.util.Arrays; 73 import java.util.Collection; 74 import java.util.List; 75 import java.util.Map; 76 import java.util.Objects; 77 import java.util.Timer; 78 import java.util.TimerTask; 79 import java.util.concurrent.TimeUnit; 80 81 /** 82 * Abstraction for vehicle HAL. This class handles interface with native HAL and does basic parsing 83 * of received data (type check). Then each event is sent to corresponding {@link HalServiceBase} 84 * implementation. It is the responsibility of {@link HalServiceBase} to convert data to 85 * corresponding Car*Service for Car*Manager API. 86 */ 87 public class VehicleHal implements VehicleHalCallback, CarSystemService { 88 private static final boolean DBG = Slogf.isLoggable(CarLog.TAG_HAL, Log.DEBUG);; 89 private static final long TRACE_TAG = TraceHelper.TRACE_TAG_CAR_SERVICE; 90 91 private static final int GLOBAL_AREA_ID = 0; 92 93 /** 94 * If call to vehicle HAL returns StatusCode.TRY_AGAIN, we will retry to invoke that method 95 * again for this amount of milliseconds. 96 */ 97 private static final int MAX_DURATION_FOR_RETRIABLE_RESULT_MS = 2000; 98 99 private static final int SLEEP_BETWEEN_RETRIABLE_INVOKES_MS = 100; 100 private static final float PRECISION_THRESHOLD = 0.001f; 101 102 private final HandlerThread mHandlerThread; 103 private final Handler mHandler; 104 private final SubscriptionClient mSubscriptionClient; 105 106 private final PowerHalService mPowerHal; 107 private final PropertyHalService mPropertyHal; 108 private final InputHalService mInputHal; 109 private final VmsHalService mVmsHal; 110 private final UserHalService mUserHal; 111 private final DiagnosticHalService mDiagnosticHal; 112 private final ClusterHalService mClusterHalService; 113 private final EvsHalService mEvsHal; 114 private final TimeHalService mTimeHalService; 115 private final HalPropValueBuilder mPropValueBuilder; 116 private final VehicleStub mVehicleStub; 117 118 private final Object mLock = new Object(); 119 120 private FeatureFlags mFeatureFlags = new FeatureFlagsImpl(); 121 122 // Only changed for test. 123 private int mMaxDurationForRetryMs = MAX_DURATION_FOR_RETRIABLE_RESULT_MS; 124 // Only changed for test. 125 private int mSleepBetweenRetryMs = SLEEP_BETWEEN_RETRIABLE_INVOKES_MS; 126 127 /** Stores handler for each HAL property. Property events are sent to handler. */ 128 @GuardedBy("mLock") 129 private final SparseArray<HalServiceBase> mPropertyHandlers = new SparseArray<>(); 130 /** This is for iterating all HalServices with fixed order. */ 131 @GuardedBy("mLock") 132 private final List<HalServiceBase> mAllServices; 133 @GuardedBy("mLock") 134 private PairSparseArray<RateInfo> mRateInfoByPropIdAreaId = new PairSparseArray<>(); 135 @GuardedBy("mLock") 136 private final SparseArray<HalPropConfig> mAllProperties = new SparseArray<>(); 137 @GuardedBy("mLock") 138 private final PairSparseArray<Integer> mAccessByPropIdAreaId = new PairSparseArray<Integer>(); 139 140 @GuardedBy("mLock") 141 private final SparseArray<VehiclePropertyEventInfo> mEventLog = new SparseArray<>(); 142 143 // Used by injectVHALEvent for testing purposes. Delimiter for an array of data 144 private static final String DATA_DELIMITER = ","; 145 146 /** A structure to store update rate in hz and whether to enable VUR. */ 147 private static final class RateInfo { 148 public float updateRateHz; 149 public boolean enableVariableUpdateRate; 150 public float resolution; 151 RateInfo(float updateRateHz, boolean enableVariableUpdateRate, float resolution)152 RateInfo(float updateRateHz, boolean enableVariableUpdateRate, float resolution) { 153 this.updateRateHz = updateRateHz; 154 this.enableVariableUpdateRate = enableVariableUpdateRate; 155 this.resolution = resolution; 156 } 157 } 158 159 /* package */ static final class HalSubscribeOptions { 160 private final int mHalPropId; 161 private final int[] mAreaIds; 162 private final float mUpdateRateHz; 163 private final boolean mEnableVariableUpdateRate; 164 private final float mResolution; 165 HalSubscribeOptions(int halPropId, int[] areaIds, float updateRateHz)166 HalSubscribeOptions(int halPropId, int[] areaIds, float updateRateHz) { 167 this(halPropId, areaIds, updateRateHz, /* enableVariableUpdateRate= */ false, 168 /* resolution= */ 0.0f); 169 } 170 HalSubscribeOptions(int halPropId, int[] areaIds, float updateRateHz, boolean enableVariableUpdateRate)171 HalSubscribeOptions(int halPropId, int[] areaIds, float updateRateHz, 172 boolean enableVariableUpdateRate) { 173 this(halPropId, areaIds, updateRateHz, enableVariableUpdateRate, 174 /* resolution= */ 0.0f); 175 } 176 HalSubscribeOptions(int halPropId, int[] areaIds, float updateRateHz, boolean enableVariableUpdateRate, float resolution)177 HalSubscribeOptions(int halPropId, int[] areaIds, float updateRateHz, 178 boolean enableVariableUpdateRate, float resolution) { 179 mHalPropId = halPropId; 180 mAreaIds = areaIds; 181 mUpdateRateHz = updateRateHz; 182 mEnableVariableUpdateRate = enableVariableUpdateRate; 183 mResolution = resolution; 184 } 185 getHalPropId()186 int getHalPropId() { 187 return mHalPropId; 188 } 189 getAreaId()190 int[] getAreaId() { 191 return mAreaIds; 192 } 193 getUpdateRateHz()194 float getUpdateRateHz() { 195 return mUpdateRateHz; 196 } 197 isVariableUpdateRateEnabled()198 boolean isVariableUpdateRateEnabled() { 199 return mEnableVariableUpdateRate; 200 } getResolution()201 float getResolution() { 202 return mResolution; 203 } 204 205 @Override equals(Object other)206 public boolean equals(Object other) { 207 if (other == this) { 208 return true; 209 } 210 211 if (!(other instanceof VehicleHal.HalSubscribeOptions)) { 212 return false; 213 } 214 215 VehicleHal.HalSubscribeOptions o = (VehicleHal.HalSubscribeOptions) other; 216 217 return mHalPropId == o.getHalPropId() && mUpdateRateHz == o.getUpdateRateHz() 218 && Arrays.equals(mAreaIds, o.getAreaId()) 219 && mEnableVariableUpdateRate == o.isVariableUpdateRateEnabled() 220 && mResolution == o.getResolution(); 221 } 222 223 @Override toString()224 public String toString() { 225 return "HalSubscribeOptions{" 226 + "PropertyId: " + mHalPropId 227 + ", AreaId: " + Arrays.toString(mAreaIds) 228 + ", UpdateRateHz: " + mUpdateRateHz 229 + ", enableVariableUpdateRate: " + mEnableVariableUpdateRate 230 + ", Resolution: " + mResolution 231 + "}"; 232 } 233 234 @Override hashCode()235 public int hashCode() { 236 return Objects.hash(mHalPropId, Arrays.hashCode(mAreaIds), mUpdateRateHz, 237 mEnableVariableUpdateRate, mResolution); 238 } 239 } 240 241 /** 242 * Constructs a new {@link VehicleHal} object given the {@link Context} and {@link IVehicle} 243 * both passed as parameters. 244 */ VehicleHal(Context context, VehicleStub vehicle)245 public VehicleHal(Context context, VehicleStub vehicle) { 246 this(context, /* powerHal= */ null, /* propertyHal= */ null, 247 /* inputHal= */ null, /* vmsHal= */ null, /* userHal= */ null, 248 /* diagnosticHal= */ null, /* clusterHalService= */ null, 249 /* timeHalService= */ null, 250 CarServiceUtils.getHandlerThread(VehicleHal.class.getSimpleName()), vehicle); 251 } 252 253 /** 254 * Constructs a new {@link VehicleHal} object given the services passed as parameters. 255 * This method must be used by tests only. 256 */ 257 @VisibleForTesting VehicleHal(Context context, PowerHalService powerHal, PropertyHalService propertyHal, InputHalService inputHal, VmsHalService vmsHal, UserHalService userHal, DiagnosticHalService diagnosticHal, ClusterHalService clusterHalService, TimeHalService timeHalService, HandlerThread handlerThread, VehicleStub vehicle)258 VehicleHal(Context context, 259 PowerHalService powerHal, 260 PropertyHalService propertyHal, 261 InputHalService inputHal, 262 VmsHalService vmsHal, 263 UserHalService userHal, 264 DiagnosticHalService diagnosticHal, 265 ClusterHalService clusterHalService, 266 TimeHalService timeHalService, 267 HandlerThread handlerThread, 268 VehicleStub vehicle) { 269 // Must be initialized before HalService so that HalService could use this. 270 mPropValueBuilder = vehicle.getHalPropValueBuilder(); 271 mHandlerThread = handlerThread; 272 mHandler = new Handler(mHandlerThread.getLooper()); 273 mPowerHal = powerHal != null ? powerHal : new PowerHalService(context, mFeatureFlags, this); 274 mPropertyHal = propertyHal != null ? propertyHal : new PropertyHalService(this); 275 mInputHal = inputHal != null ? inputHal : new InputHalService(this); 276 mVmsHal = vmsHal != null ? vmsHal : new VmsHalService(context, this); 277 mUserHal = userHal != null ? userHal : new UserHalService(this); 278 mDiagnosticHal = diagnosticHal != null ? diagnosticHal : new DiagnosticHalService(this); 279 mClusterHalService = clusterHalService != null 280 ? clusterHalService : new ClusterHalService(context, this); 281 mEvsHal = new EvsHalService(this); 282 mTimeHalService = timeHalService != null 283 ? timeHalService : new TimeHalService(context, this); 284 mAllServices = List.of( 285 mPowerHal, 286 mInputHal, 287 mDiagnosticHal, 288 mVmsHal, 289 mUserHal, 290 mClusterHalService, 291 mEvsHal, 292 mTimeHalService, 293 // mPropertyHal must be the last so that on init/release it can be used for all 294 // other HAL services properties. 295 mPropertyHal); 296 mVehicleStub = vehicle; 297 mSubscriptionClient = vehicle.newSubscriptionClient(this); 298 } 299 300 /** Sets fake feature flag for unit testing. */ 301 @VisibleForTesting setFeatureFlags(FeatureFlags fakeFeatureFlags)302 public void setFeatureFlags(FeatureFlags fakeFeatureFlags) { 303 mFeatureFlags = fakeFeatureFlags; 304 } 305 306 @VisibleForTesting setMaxDurationForRetryMs(int maxDurationForRetryMs)307 void setMaxDurationForRetryMs(int maxDurationForRetryMs) { 308 mMaxDurationForRetryMs = maxDurationForRetryMs; 309 } 310 311 @VisibleForTesting setSleepBetweenRetryMs(int sleepBetweenRetryMs)312 void setSleepBetweenRetryMs(int sleepBetweenRetryMs) { 313 mSleepBetweenRetryMs = sleepBetweenRetryMs; 314 } 315 316 @VisibleForTesting fetchAllPropConfigs()317 void fetchAllPropConfigs() { 318 synchronized (mLock) { 319 if (mAllProperties.size() != 0) { // already set 320 Slogf.i(CarLog.TAG_HAL, "fetchAllPropConfigs already fetched"); 321 return; 322 } 323 } 324 HalPropConfig[] configs; 325 try { 326 configs = getAllPropConfigs(); 327 if (configs == null || configs.length == 0) { 328 Slogf.e(CarLog.TAG_HAL, "getAllPropConfigs returned empty configs"); 329 return; 330 } 331 } catch (RemoteException | ServiceSpecificException e) { 332 throw new RuntimeException("Unable to retrieve vehicle property configuration", e); 333 } 334 335 synchronized (mLock) { 336 // Create map of all properties 337 for (HalPropConfig p : configs) { 338 if (DBG) { 339 Slogf.d(CarLog.TAG_HAL, "Add config for prop: 0x%x config: %s", p.getPropId(), 340 p.toString()); 341 } 342 mAllProperties.put(p.getPropId(), p); 343 if (p.getAreaConfigs().length == 0) { 344 mAccessByPropIdAreaId.put(p.getPropId(), /* areaId */ 0, p.getAccess()); 345 } else { 346 for (HalAreaConfig areaConfig : p.getAreaConfigs()) { 347 mAccessByPropIdAreaId.put(p.getPropId(), areaConfig.getAreaId(), 348 areaConfig.getAccess()); 349 } 350 } 351 } 352 } 353 } 354 handleOnPropertyEvent(List<HalPropValue> propValues)355 private void handleOnPropertyEvent(List<HalPropValue> propValues) { 356 synchronized (mLock) { 357 for (int i = 0; i < propValues.size(); i++) { 358 HalPropValue v = propValues.get(i); 359 int propId = v.getPropId(); 360 HalServiceBase service = mPropertyHandlers.get(propId); 361 if (service == null) { 362 Slogf.e(CarLog.TAG_HAL, "handleOnPropertyEvent: HalService not found for %s", 363 v); 364 continue; 365 } 366 service.getDispatchList().add(v); 367 mServicesToDispatch.add(service); 368 VehiclePropertyEventInfo info = mEventLog.get(propId); 369 if (info == null) { 370 info = new VehiclePropertyEventInfo(v); 371 mEventLog.put(propId, info); 372 } else { 373 info.addNewEvent(v); 374 } 375 } 376 } 377 for (HalServiceBase s : mServicesToDispatch) { 378 s.onHalEvents(s.getDispatchList()); 379 s.getDispatchList().clear(); 380 } 381 mServicesToDispatch.clear(); 382 } 383 handleOnPropertySetError(List<VehiclePropError> errors)384 private void handleOnPropertySetError(List<VehiclePropError> errors) { 385 SparseArray<ArrayList<VehiclePropError>> errorsByPropId = 386 new SparseArray<ArrayList<VehiclePropError>>(); 387 for (int i = 0; i < errors.size(); i++) { 388 VehiclePropError error = errors.get(i); 389 int errorCode = error.errorCode; 390 int propId = error.propId; 391 int areaId = error.areaId; 392 Slogf.w(CarLog.TAG_HAL, "onPropertySetError, errorCode: %d, prop: 0x%x, area: 0x%x", 393 errorCode, propId, areaId); 394 if (propId == VehicleProperty.INVALID) { 395 continue; 396 } 397 398 ArrayList<VehiclePropError> propErrors; 399 if (errorsByPropId.get(propId) == null) { 400 propErrors = new ArrayList<VehiclePropError>(); 401 errorsByPropId.put(propId, propErrors); 402 } else { 403 propErrors = errorsByPropId.get(propId); 404 } 405 propErrors.add(error); 406 } 407 408 for (int i = 0; i < errorsByPropId.size(); i++) { 409 int propId = errorsByPropId.keyAt(i); 410 HalServiceBase service; 411 synchronized (mLock) { 412 service = mPropertyHandlers.get(propId); 413 } 414 if (service == null) { 415 Slogf.e(CarLog.TAG_HAL, 416 "handleOnPropertySetError: HalService not found for prop: 0x%x", propId); 417 continue; 418 } 419 420 ArrayList<VehiclePropError> propErrors = errorsByPropId.get(propId); 421 service.onPropertySetError(propErrors); 422 } 423 } 424 errorMessage(String action, HalPropValue propValue, String errorMsg)425 private static String errorMessage(String action, HalPropValue propValue, String errorMsg) { 426 return String.format("Failed to %s value for: %s, error: %s", action, 427 propValue, errorMsg); 428 } 429 getValueWithRetry(HalPropValue value)430 private HalPropValue getValueWithRetry(HalPropValue value) { 431 return getValueWithRetry(value, /* maxRetries= */ 0); 432 } 433 getValueWithRetry(HalPropValue value, int maxRetries)434 private HalPropValue getValueWithRetry(HalPropValue value, int maxRetries) { 435 HalPropValue result; 436 Trace.traceBegin(TRACE_TAG, "VehicleStub#getValueWithRetry"); 437 try { 438 result = invokeRetriable((requestValue) -> { 439 Trace.traceBegin(TRACE_TAG, "VehicleStub#get"); 440 try { 441 return mVehicleStub.get(requestValue); 442 } finally { 443 Trace.traceEnd(TRACE_TAG); 444 } 445 }, "get", value, mMaxDurationForRetryMs, mSleepBetweenRetryMs, maxRetries); 446 } finally { 447 Trace.traceEnd(TRACE_TAG); 448 } 449 450 if (result == null) { 451 // If VHAL returns null result, but the status is OKAY. We treat that as NOT_AVAILABLE. 452 throw new ServiceSpecificException(StatusCode.NOT_AVAILABLE, 453 errorMessage("get", value, "VHAL returns null for property value")); 454 } 455 return result; 456 } 457 setValueWithRetry(HalPropValue value)458 private void setValueWithRetry(HalPropValue value) { 459 invokeRetriable((requestValue) -> { 460 Trace.traceBegin(TRACE_TAG, "VehicleStub#set"); 461 mVehicleStub.set(requestValue); 462 Trace.traceEnd(TRACE_TAG); 463 return null; 464 }, "set", value, mMaxDurationForRetryMs, mSleepBetweenRetryMs, /* maxRetries= */ 0); 465 } 466 467 /** 468 * Inits the vhal configurations. 469 */ 470 @Override init()471 public void init() { 472 // nothing to init as everything was done on priorityInit 473 } 474 475 /** 476 * PriorityInit for the vhal configurations. 477 */ priorityInit()478 public void priorityInit() { 479 fetchAllPropConfigs(); 480 481 // PropertyHalService will take most properties, so make it big enough. 482 ArrayMap<HalServiceBase, ArrayList<HalPropConfig>> configsForAllServices; 483 synchronized (mLock) { 484 configsForAllServices = new ArrayMap<>(mAllServices.size()); 485 for (int i = 0; i < mAllServices.size(); i++) { 486 ArrayList<HalPropConfig> configsForService = new ArrayList(); 487 HalServiceBase service = mAllServices.get(i); 488 configsForAllServices.put(service, configsForService); 489 int[] supportedProps = service.getAllSupportedProperties(); 490 if (supportedProps.length == 0) { 491 for (int j = 0; j < mAllProperties.size(); j++) { 492 Integer propId = mAllProperties.keyAt(j); 493 if (service.isSupportedProperty(propId)) { 494 HalPropConfig config = mAllProperties.get(propId); 495 mPropertyHandlers.append(propId, service); 496 configsForService.add(config); 497 } 498 } 499 } else { 500 for (int prop : supportedProps) { 501 HalPropConfig config = mAllProperties.get(prop); 502 if (config == null) { 503 continue; 504 } 505 mPropertyHandlers.append(prop, service); 506 configsForService.add(config); 507 } 508 } 509 } 510 } 511 512 for (Map.Entry<HalServiceBase, ArrayList<HalPropConfig>> entry 513 : configsForAllServices.entrySet()) { 514 HalServiceBase service = entry.getKey(); 515 ArrayList<HalPropConfig> configsForService = entry.getValue(); 516 service.takeProperties(configsForService); 517 service.init(); 518 } 519 } 520 521 /** 522 * Releases all connected services (power management service, input service, etc). 523 */ 524 @Override release()525 public void release() { 526 ArraySet<Integer> subscribedProperties = new ArraySet<>(); 527 synchronized (mLock) { 528 // release in reverse order from init 529 for (int i = mAllServices.size() - 1; i >= 0; i--) { 530 mAllServices.get(i).release(); 531 } 532 for (int i = 0; i < mRateInfoByPropIdAreaId.size(); i++) { 533 int propertyId = mRateInfoByPropIdAreaId.keyPairAt(i)[0]; 534 subscribedProperties.add(propertyId); 535 } 536 mRateInfoByPropIdAreaId.clear(); 537 mAllProperties.clear(); 538 mAccessByPropIdAreaId.clear(); 539 } 540 for (int i = 0; i < subscribedProperties.size(); i++) { 541 try { 542 mSubscriptionClient.unsubscribe(subscribedProperties.valueAt(i)); 543 } catch (RemoteException | ServiceSpecificException e) { 544 // Ignore exceptions on shutdown path. 545 Slogf.w(CarLog.TAG_HAL, "Failed to unsubscribe", e); 546 } 547 } 548 // keep the looper thread as should be kept for the whole life cycle. 549 } 550 getDiagnosticHal()551 public DiagnosticHalService getDiagnosticHal() { 552 return mDiagnosticHal; 553 } 554 getPowerHal()555 public PowerHalService getPowerHal() { 556 return mPowerHal; 557 } 558 getPropertyHal()559 public PropertyHalService getPropertyHal() { 560 return mPropertyHal; 561 } 562 getInputHal()563 public InputHalService getInputHal() { 564 return mInputHal; 565 } 566 getUserHal()567 public UserHalService getUserHal() { 568 return mUserHal; 569 } 570 getVmsHal()571 public VmsHalService getVmsHal() { 572 return mVmsHal; 573 } 574 getClusterHal()575 public ClusterHalService getClusterHal() { 576 return mClusterHalService; 577 } 578 getEvsHal()579 public EvsHalService getEvsHal() { 580 return mEvsHal; 581 } 582 getTimeHalService()583 public TimeHalService getTimeHalService() { 584 return mTimeHalService; 585 } 586 getHalPropValueBuilder()587 public HalPropValueBuilder getHalPropValueBuilder() { 588 return mPropValueBuilder; 589 } 590 591 @GuardedBy("mLock") assertServiceOwnerLocked(HalServiceBase service, int property)592 private void assertServiceOwnerLocked(HalServiceBase service, int property) { 593 if (service != mPropertyHandlers.get(property)) { 594 throw new IllegalArgumentException(String.format( 595 "Property 0x%x is not owned by service: %s", property, service)); 596 } 597 } 598 599 /** 600 * Subscribes given properties with sampling rate defaults to 0 and no special flags provided. 601 * 602 * @throws IllegalArgumentException thrown if property is not supported by VHAL 603 * @throws ServiceSpecificException if VHAL returns error or lost connection with VHAL. 604 * @see #subscribeProperty(HalServiceBase, int, float) 605 */ subscribeProperty(HalServiceBase service, int property)606 public void subscribeProperty(HalServiceBase service, int property) 607 throws IllegalArgumentException, ServiceSpecificException { 608 subscribeProperty(service, property, /* samplingRateHz= */ 0f); 609 } 610 611 /** 612 * Similar to {@link #subscribeProperty(HalServiceBase, int)} except that all exceptions 613 * are caught and are logged. 614 */ subscribePropertySafe(HalServiceBase service, int property)615 public void subscribePropertySafe(HalServiceBase service, int property) { 616 try { 617 subscribeProperty(service, property); 618 } catch (IllegalArgumentException | ServiceSpecificException e) { 619 Slogf.w(CarLog.TAG_HAL, "Failed to subscribe for property: " 620 + VehiclePropertyIds.toString(property), e); 621 } 622 } 623 624 /** 625 * Subscribe given property. Only Hal service owning the property can subscribe it. 626 * 627 * @param service HalService that owns this property 628 * @param property property id (VehicleProperty) 629 * @param samplingRateHz sampling rate in Hz for continuous properties 630 * @throws IllegalArgumentException thrown if property is not supported by VHAL 631 * @throws ServiceSpecificException if VHAL returns error or lost connection with VHAL. 632 */ subscribeProperty(HalServiceBase service, int property, float samplingRateHz)633 public void subscribeProperty(HalServiceBase service, int property, float samplingRateHz) 634 throws IllegalArgumentException, ServiceSpecificException { 635 HalSubscribeOptions options = new HalSubscribeOptions(property, new int[0], samplingRateHz); 636 subscribeProperty(service, List.of(options)); 637 } 638 639 /** 640 * Similar to {@link #subscribeProperty(HalServiceBase, int, float)} except that all exceptions 641 * are caught and converted to logs. 642 */ subscribePropertySafe(HalServiceBase service, int property, float sampleRateHz)643 public void subscribePropertySafe(HalServiceBase service, int property, float sampleRateHz) { 644 try { 645 subscribeProperty(service, property, sampleRateHz); 646 } catch (IllegalArgumentException | ServiceSpecificException e) { 647 Slogf.w(CarLog.TAG_HAL, e, "Failed to subscribe for property: %s, sample rate: %f hz", 648 VehiclePropertyIds.toString(property), sampleRateHz); 649 } 650 } 651 652 /** 653 * Subscribe given property. Only Hal service owning the property can subscribe it. 654 * 655 * @param service HalService that owns this property 656 * @param halSubscribeOptions Information needed to subscribe to VHAL 657 * @throws IllegalArgumentException thrown if property is not supported by VHAL 658 * @throws ServiceSpecificException if VHAL returns error or lost connection with VHAL. 659 */ subscribeProperty(HalServiceBase service, List<HalSubscribeOptions> halSubscribeOptions)660 public void subscribeProperty(HalServiceBase service, List<HalSubscribeOptions> 661 halSubscribeOptions) throws IllegalArgumentException, ServiceSpecificException { 662 synchronized (mLock) { 663 PairSparseArray<RateInfo> previousState = cloneState(mRateInfoByPropIdAreaId); 664 SubscribeOptions[] subscribeOptions = createVhalSubscribeOptionsLocked( 665 service, halSubscribeOptions); 666 if (subscribeOptions.length == 0) { 667 if (DBG) { 668 Slogf.d(CarLog.TAG_HAL, 669 "Ignore the subscribeProperty request, SubscribeOptions is length 0"); 670 } 671 return; 672 } 673 try { 674 mSubscriptionClient.subscribe(subscribeOptions); 675 } catch (RemoteException e) { 676 mRateInfoByPropIdAreaId = previousState; 677 Slogf.w(CarLog.TAG_HAL, "Failed to subscribe, connection to VHAL failed", e); 678 // Convert RemoteException to ServiceSpecificException so that it could be passed 679 // back to the client. 680 throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR, 681 "Failed to subscribe, connection to VHAL failed, error: " + e); 682 } catch (ServiceSpecificException e) { 683 mRateInfoByPropIdAreaId = previousState; 684 Slogf.w(CarLog.TAG_HAL, "Failed to subscribe, received error from VHAL", e); 685 throw e; 686 } 687 } 688 } 689 690 /** 691 * Converts {@link HalSubscribeOptions} to {@link SubscribeOptions} which is the data structure 692 * used by VHAL. 693 */ 694 @GuardedBy("mLock") createVhalSubscribeOptionsLocked(HalServiceBase service, List<HalSubscribeOptions> halSubscribeOptions)695 private SubscribeOptions[] createVhalSubscribeOptionsLocked(HalServiceBase service, 696 List<HalSubscribeOptions> halSubscribeOptions) throws IllegalArgumentException { 697 if (DBG) { 698 Slogf.d(CarLog.TAG_HAL, "creating subscribeOptions from HalSubscribeOptions of size: " 699 + halSubscribeOptions.size()); 700 } 701 List<SubscribeOptions> subscribeOptionsList = new ArrayList<>(); 702 for (int i = 0; i < halSubscribeOptions.size(); i++) { 703 HalSubscribeOptions halSubscribeOption = halSubscribeOptions.get(i); 704 int property = halSubscribeOption.getHalPropId(); 705 int[] areaIds = halSubscribeOption.getAreaId(); 706 float samplingRateHz = halSubscribeOption.getUpdateRateHz(); 707 boolean enableVariableUpdateRate = halSubscribeOption.isVariableUpdateRateEnabled(); 708 float resolution = halSubscribeOption.getResolution(); 709 710 HalPropConfig config; 711 config = mAllProperties.get(property); 712 713 if (config == null) { 714 throw new IllegalArgumentException("subscribe error: " 715 + toPropertyIdString(property) + " is not supported"); 716 } 717 718 if (enableVariableUpdateRate) { 719 if (config.getChangeMode() != VehiclePropertyChangeMode.CONTINUOUS) { 720 // enableVur should be ignored if property is not continuous, but we set it to 721 // false to be safe. 722 enableVariableUpdateRate = false; 723 Slogf.w(CarLog.TAG_HAL, "VUR is always off for non-continuous property: " 724 + toPropertyIdString(property)); 725 } 726 if (!mFeatureFlags.variableUpdateRate()) { 727 enableVariableUpdateRate = false; 728 Slogf.w(CarLog.TAG_HAL, "VUR feature is not enabled, VUR is always off"); 729 } 730 } 731 732 if (resolution != 0.0f) { 733 if (config.getChangeMode() != VehiclePropertyChangeMode.CONTINUOUS) { 734 // resolution should be ignored if property is not continuous, but we set it to 735 // 0 to be safe. 736 resolution = 0.0f; 737 Slogf.w(CarLog.TAG_HAL, "resolution is always 0 for non-continuous property: " 738 + toPropertyIdString(property)); 739 } 740 if (!mFeatureFlags.subscriptionWithResolution()) { 741 resolution = 0.0f; 742 Slogf.w(CarLog.TAG_HAL, 743 "Resolution feature is not enabled, resolution is always 0"); 744 } 745 } 746 747 if (isStaticProperty(config)) { 748 Slogf.w(CarLog.TAG_HAL, "Ignore subscribing to static property: " 749 + toPropertyIdString(property)); 750 continue; 751 } 752 753 if (areaIds.length == 0) { 754 if (!isPropertySubscribable(config)) { 755 throw new IllegalArgumentException("Property: " + toPropertyIdString(property) 756 + " is not subscribable"); 757 } 758 areaIds = getAllAreaIdsFromPropertyId(config); 759 } else { 760 for (int j = 0; j < areaIds.length; j++) { 761 Integer access = mAccessByPropIdAreaId.get(config.getPropId(), areaIds[j]); 762 if (access == null) { 763 throw new IllegalArgumentException( 764 "Cannot subscribe to " + toPropertyIdString(property) 765 + " at areaId " + toAreaIdString(property, areaIds[j]) 766 + " the property does not have the requested areaId"); 767 } 768 if (!isPropIdAreaIdReadable(config, access.intValue())) { 769 throw new IllegalArgumentException( 770 "Cannot subscribe to " + toPropertyIdString(property) 771 + " at areaId " + toAreaIdString(property, areaIds[j]) 772 + " the property's access mode does not contain READ"); 773 } 774 } 775 } 776 SubscribeOptions opts = new SubscribeOptions(); 777 opts.propId = property; 778 opts.sampleRate = samplingRateHz; 779 opts.enableVariableUpdateRate = enableVariableUpdateRate; 780 opts.resolution = resolution; 781 RateInfo rateInfo = new RateInfo(samplingRateHz, enableVariableUpdateRate, resolution); 782 int[] filteredAreaIds = filterAreaIdsWithSameRateInfo(property, areaIds, rateInfo); 783 opts.areaIds = filteredAreaIds; 784 if (opts.areaIds.length == 0) { 785 if (DBG) { 786 Slogf.d(CarLog.TAG_HAL, "property: " + VehiclePropertyIds.toString(property) 787 + " is already subscribed at rate: " + samplingRateHz + " hz"); 788 } 789 continue; 790 } 791 assertServiceOwnerLocked(service, property); 792 for (int j = 0; j < filteredAreaIds.length; j++) { 793 if (DBG) { 794 Slogf.d(CarLog.TAG_HAL, "Update subscription rate for propertyId:" 795 + " %s, areaId: %d, SampleRateHz: %f, enableVur: %b," 796 + " resolution: %f", 797 VehiclePropertyIds.toString(opts.propId), filteredAreaIds[j], 798 samplingRateHz, enableVariableUpdateRate, resolution); 799 } 800 mRateInfoByPropIdAreaId.put(property, filteredAreaIds[j], rateInfo); 801 } 802 subscribeOptionsList.add(opts); 803 } 804 return subscribeOptionsList.toArray(new SubscribeOptions[0]); 805 } 806 filterAreaIdsWithSameRateInfo(int property, int[] areaIds, RateInfo rateInfo)807 private int[] filterAreaIdsWithSameRateInfo(int property, int[] areaIds, RateInfo rateInfo) { 808 List<Integer> areaIdList = new ArrayList<>(); 809 synchronized (mLock) { 810 for (int i = 0; i < areaIds.length; i++) { 811 RateInfo savedRateInfo = mRateInfoByPropIdAreaId.get(property, areaIds[i]); 812 813 // Strict equality (==) is used here for comparing resolutions. This approach does 814 // not introduce a margin of error through PRECISION_THRESHOLD, and thus can allow 815 // clients to request the highest possible resolution without being limited by a 816 // predefined threshold. This approach is assumed to be feasible under the 817 // hypothesis that the floating point representation of numbers is consistent 818 // across the system. That is, if two clients specify a resolution of 0.01f, 819 // their internal representations will match, enabling an exact comparison despite 820 // floating point inaccuracies. If this is inaccurate, we must introduce a margin 821 // of error (ideally 1e-7 as floats can reliably represent up to 7 significant 822 // figures, but can be higher if necessary), and update the documentation in {@link 823 // android.car.hardware.property.Subscription.Builder#setResolution(float)} 824 // appropriately. 825 if (savedRateInfo != null 826 && (Math.abs(savedRateInfo.updateRateHz - rateInfo.updateRateHz) 827 < PRECISION_THRESHOLD) 828 && (savedRateInfo.enableVariableUpdateRate 829 == rateInfo.enableVariableUpdateRate) 830 && savedRateInfo.resolution == rateInfo.resolution) { 831 if (DBG) { 832 Slogf.d(CarLog.TAG_HAL, "Property: %s is already subscribed at rate: %f hz" 833 + ", enableVur: %b, resolution: %f", 834 toPropertyIdString(property), rateInfo.updateRateHz, 835 rateInfo.enableVariableUpdateRate, rateInfo.resolution); 836 } 837 continue; 838 } 839 areaIdList.add(areaIds[i]); 840 } 841 } 842 return CarServiceUtils.toIntArray(areaIdList); 843 } 844 getAllAreaIdsFromPropertyId(HalPropConfig config)845 private int[] getAllAreaIdsFromPropertyId(HalPropConfig config) { 846 HalAreaConfig[] allAreaConfigs = config.getAreaConfigs(); 847 if (allAreaConfigs.length == 0) { 848 return new int[]{/* areaId= */ 0}; 849 } 850 int[] areaId = new int[allAreaConfigs.length]; 851 for (int i = 0; i < allAreaConfigs.length; i++) { 852 areaId[i] = allAreaConfigs[i].getAreaId(); 853 } 854 return areaId; 855 } 856 857 /** 858 * Like {@link unsubscribeProperty} except that exceptions are logged. 859 */ unsubscribePropertySafe(HalServiceBase service, int property)860 public void unsubscribePropertySafe(HalServiceBase service, int property) { 861 try { 862 unsubscribeProperty(service, property); 863 } catch (ServiceSpecificException e) { 864 Slogf.w(CarLog.TAG_SERVICE, "Failed to unsubscribe: " 865 + toPropertyIdString(property), e); 866 } 867 } 868 869 /** 870 * Unsubscribes from receiving notifications for the property and HAL services passed 871 * as parameters. 872 */ unsubscribeProperty(HalServiceBase service, int property)873 public void unsubscribeProperty(HalServiceBase service, int property) 874 throws ServiceSpecificException { 875 if (DBG) { 876 Slogf.d(CarLog.TAG_HAL, "unsubscribeProperty, service:" + service 877 + ", " + toPropertyIdString(property)); 878 } 879 synchronized (mLock) { 880 HalPropConfig config = mAllProperties.get(property); 881 if (config == null) { 882 Slogf.w(CarLog.TAG_HAL, "unsubscribeProperty " + toPropertyIdString(property) 883 + " does not exist"); 884 return; 885 } 886 if (isStaticProperty(config)) { 887 Slogf.w(CarLog.TAG_HAL, "Unsubscribe to a static property: " 888 + toPropertyIdString(property) + ", do nothing"); 889 return; 890 } 891 assertServiceOwnerLocked(service, property); 892 HalAreaConfig[] halAreaConfigs = config.getAreaConfigs(); 893 boolean isSubscribed = false; 894 PairSparseArray<RateInfo> previousState = cloneState(mRateInfoByPropIdAreaId); 895 if (halAreaConfigs.length == 0) { 896 int index = mRateInfoByPropIdAreaId.indexOfKeyPair(property, 0); 897 if (hasReadAccess(config.getAccess()) && index >= 0) { 898 mRateInfoByPropIdAreaId.removeAt(index); 899 isSubscribed = true; 900 } 901 } else { 902 for (int i = 0; i < halAreaConfigs.length; i++) { 903 if (!isPropIdAreaIdReadable(config, halAreaConfigs[i].getAccess())) { 904 Slogf.w(CarLog.TAG_HAL, 905 "Cannot unsubscribe to " + toPropertyIdString(property) 906 + " at areaId " + toAreaIdString(property, 907 halAreaConfigs[i].getAreaId()) 908 + " the property's access mode does not contain READ"); 909 continue; 910 } 911 int index = mRateInfoByPropIdAreaId.indexOfKeyPair(property, 912 halAreaConfigs[i].getAreaId()); 913 if (index >= 0) { 914 mRateInfoByPropIdAreaId.removeAt(index); 915 isSubscribed = true; 916 } 917 } 918 } 919 if (!isSubscribed) { 920 if (DBG) { 921 Slogf.d(CarLog.TAG_HAL, "Property " + toPropertyIdString(property) 922 + " was not subscribed, do nothing"); 923 } 924 return; 925 } 926 try { 927 mSubscriptionClient.unsubscribe(property); 928 } catch (RemoteException e) { 929 mRateInfoByPropIdAreaId = previousState; 930 Slogf.w(CarLog.TAG_HAL, "Failed to unsubscribe, connection to VHAL failed", e); 931 throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR, 932 "Failed to unsubscribe, connection to VHAL failed, error: " + e); 933 } catch (ServiceSpecificException e) { 934 mRateInfoByPropIdAreaId = previousState; 935 Slogf.w(CarLog.TAG_HAL, "Failed to unsubscribe, received error from VHAL", e); 936 throw e; 937 } 938 } 939 } 940 941 /** 942 * Indicates if the property passed as parameter is supported. 943 */ isPropertySupported(int propertyId)944 public boolean isPropertySupported(int propertyId) { 945 synchronized (mLock) { 946 return mAllProperties.contains(propertyId); 947 } 948 } 949 950 /** 951 * Gets given property with retries. 952 * 953 * <p>If getting the property fails after all retries, it will throw 954 * {@code IllegalStateException}. If the property is not supported, it will simply return 955 * {@code null}. 956 */ 957 @Nullable getIfSupportedOrFail(int propertyId, int maxRetries)958 public HalPropValue getIfSupportedOrFail(int propertyId, int maxRetries) { 959 if (!isPropertySupported(propertyId)) { 960 return null; 961 } 962 try { 963 return getValueWithRetry(mPropValueBuilder.build(propertyId, GLOBAL_AREA_ID), 964 maxRetries); 965 } catch (Exception e) { 966 throw new IllegalStateException(e); 967 } 968 } 969 970 /** 971 * This works similar to {@link #getIfSupportedOrFail(int, int)} except that this can be called 972 * before {@code init()} is called. 973 * 974 * <p>This call will check if requested vhal property is supported by querying directly to vhal 975 * and can have worse performance. Use this only for accessing vhal properties before 976 * {@code ICarImpl.init()} phase. 977 */ 978 @Nullable getIfSupportedOrFailForEarlyStage(int propertyId, int maxRetries)979 public HalPropValue getIfSupportedOrFailForEarlyStage(int propertyId, int maxRetries) { 980 fetchAllPropConfigs(); 981 return getIfSupportedOrFail(propertyId, maxRetries); 982 } 983 984 /** 985 * Returns the property's {@link HalPropValue} for the property id passed as parameter and 986 * not specified area. 987 * 988 * @throws IllegalArgumentException if argument is invalid 989 * @throws ServiceSpecificException if VHAL returns error 990 */ get(int propertyId)991 public HalPropValue get(int propertyId) 992 throws IllegalArgumentException, ServiceSpecificException { 993 return get(propertyId, GLOBAL_AREA_ID); 994 } 995 996 /** 997 * Returns the property's {@link HalPropValue} for the property id and area id passed as 998 * parameters. 999 * 1000 * @throws IllegalArgumentException if argument is invalid 1001 * @throws ServiceSpecificException if VHAL returns error 1002 */ get(int propertyId, int areaId)1003 public HalPropValue get(int propertyId, int areaId) 1004 throws IllegalArgumentException, ServiceSpecificException { 1005 if (DBG) { 1006 Slogf.d(CarLog.TAG_HAL, "get, " + toPropertyIdString(propertyId) 1007 + toAreaIdString(propertyId, areaId)); 1008 } 1009 return getValueWithRetry(mPropValueBuilder.build(propertyId, areaId)); 1010 } 1011 1012 /** 1013 * Returns the property object value for the class and property id passed as parameter and 1014 * no area specified. 1015 * 1016 * @throws IllegalArgumentException if argument is invalid 1017 * @throws ServiceSpecificException if VHAL returns error 1018 */ get(Class clazz, int propertyId)1019 public <T> T get(Class clazz, int propertyId) 1020 throws IllegalArgumentException, ServiceSpecificException { 1021 return get(clazz, propertyId, GLOBAL_AREA_ID); 1022 } 1023 1024 /** 1025 * Returns the property object value for the class, property id, and area id passed as 1026 * parameter. 1027 * 1028 * @throws IllegalArgumentException if argument is invalid 1029 * @throws ServiceSpecificException if VHAL returns error 1030 */ get(Class clazz, int propertyId, int areaId)1031 public <T> T get(Class clazz, int propertyId, int areaId) 1032 throws IllegalArgumentException, ServiceSpecificException { 1033 return get(clazz, mPropValueBuilder.build(propertyId, areaId)); 1034 } 1035 1036 /** 1037 * Returns the property object value for the class and requested property value passed as 1038 * parameter. 1039 * 1040 * @throws IllegalArgumentException if argument is invalid 1041 * @throws ServiceSpecificException if VHAL returns error 1042 */ 1043 @SuppressWarnings("unchecked") get(Class clazz, HalPropValue requestedPropValue)1044 public <T> T get(Class clazz, HalPropValue requestedPropValue) 1045 throws IllegalArgumentException, ServiceSpecificException { 1046 HalPropValue propValue; 1047 propValue = getValueWithRetry(requestedPropValue); 1048 1049 if (clazz == Long.class || clazz == long.class) { 1050 Long value = propValue.getInt64Value(0); 1051 return (T) value; 1052 } else if (clazz == Integer.class || clazz == int.class) { 1053 Integer value = propValue.getInt32Value(0); 1054 return (T) value; 1055 } else if (clazz == Boolean.class || clazz == boolean.class) { 1056 Boolean value = Boolean.valueOf(propValue.getInt32Value(0) == 1); 1057 return (T) value; 1058 } else if (clazz == Float.class || clazz == float.class) { 1059 Float value = propValue.getFloatValue(0); 1060 return (T) value; 1061 } else if (clazz == Long[].class) { 1062 int size = propValue.getInt64ValuesSize(); 1063 Long[] longArray = new Long[size]; 1064 for (int i = 0; i < size; i++) { 1065 longArray[i] = propValue.getInt64Value(i); 1066 } 1067 return (T) longArray; 1068 } else if (clazz == Integer[].class) { 1069 int size = propValue.getInt32ValuesSize(); 1070 Integer[] intArray = new Integer[size]; 1071 for (int i = 0; i < size; i++) { 1072 intArray[i] = propValue.getInt32Value(i); 1073 } 1074 return (T) intArray; 1075 } else if (clazz == Float[].class) { 1076 int size = propValue.getFloatValuesSize(); 1077 Float[] floatArray = new Float[size]; 1078 for (int i = 0; i < size; i++) { 1079 floatArray[i] = propValue.getFloatValue(i); 1080 } 1081 return (T) floatArray; 1082 } else if (clazz == long[].class) { 1083 int size = propValue.getInt64ValuesSize(); 1084 long[] longArray = new long[size]; 1085 for (int i = 0; i < size; i++) { 1086 longArray[i] = propValue.getInt64Value(i); 1087 } 1088 return (T) longArray; 1089 } else if (clazz == int[].class) { 1090 int size = propValue.getInt32ValuesSize(); 1091 int[] intArray = new int[size]; 1092 for (int i = 0; i < size; i++) { 1093 intArray[i] = propValue.getInt32Value(i); 1094 } 1095 return (T) intArray; 1096 } else if (clazz == float[].class) { 1097 int size = propValue.getFloatValuesSize(); 1098 float[] floatArray = new float[size]; 1099 for (int i = 0; i < size; i++) { 1100 floatArray[i] = propValue.getFloatValue(i); 1101 } 1102 return (T) floatArray; 1103 } else if (clazz == byte[].class) { 1104 return (T) propValue.getByteArray(); 1105 } else if (clazz == String.class) { 1106 return (T) propValue.getStringValue(); 1107 } else { 1108 throw new IllegalArgumentException("Unexpected type: " + clazz); 1109 } 1110 } 1111 1112 /** 1113 * Returns the vehicle's {@link HalPropValue} for the requested property value passed 1114 * as parameter. 1115 * 1116 * @throws IllegalArgumentException if argument is invalid 1117 * @throws ServiceSpecificException if VHAL returns error 1118 */ get(HalPropValue requestedPropValue)1119 public HalPropValue get(HalPropValue requestedPropValue) 1120 throws IllegalArgumentException, ServiceSpecificException { 1121 return getValueWithRetry(requestedPropValue); 1122 } 1123 1124 /** 1125 * Set property. 1126 * 1127 * @throws IllegalArgumentException if argument is invalid 1128 * @throws ServiceSpecificException if VHAL returns error 1129 */ set(HalPropValue propValue)1130 public void set(HalPropValue propValue) 1131 throws IllegalArgumentException, ServiceSpecificException { 1132 setValueWithRetry(propValue); 1133 } 1134 1135 @CheckResult set(int propId)1136 HalPropValueSetter set(int propId) { 1137 return set(propId, GLOBAL_AREA_ID); 1138 } 1139 1140 @CheckResult set(int propId, int areaId)1141 HalPropValueSetter set(int propId, int areaId) { 1142 return new HalPropValueSetter(propId, areaId); 1143 } 1144 hasReadAccess(int accessLevel)1145 private static boolean hasReadAccess(int accessLevel) { 1146 return accessLevel == VehiclePropertyAccess.READ 1147 || accessLevel == VehiclePropertyAccess.READ_WRITE; 1148 } 1149 isPropIdAreaIdReadable(HalPropConfig config, int areaIdAccess)1150 private static boolean isPropIdAreaIdReadable(HalPropConfig config, int areaIdAccess) { 1151 return (areaIdAccess == VehiclePropertyAccess.NONE) 1152 ? hasReadAccess(config.getAccess()) : hasReadAccess(areaIdAccess); 1153 } 1154 1155 /** 1156 * Returns whether the property is readable and not static. 1157 */ isPropertySubscribable(HalPropConfig config)1158 static boolean isPropertySubscribable(HalPropConfig config) { 1159 if (isStaticProperty(config)) { 1160 Slogf.w(CarLog.TAG_HAL, "Subscribe to a static property: " 1161 + toPropertyIdString(config.getPropId()) + ", do nothing"); 1162 return false; 1163 } 1164 if (config.getAreaConfigs().length == 0) { 1165 boolean hasReadAccess = hasReadAccess(config.getAccess()); 1166 if (!hasReadAccess) { 1167 Slogf.w(CarLog.TAG_HAL, "Cannot subscribe to " 1168 + toPropertyIdString(config.getPropId()) 1169 + " the property's access mode does not contain READ"); 1170 } 1171 return hasReadAccess; 1172 } 1173 for (HalAreaConfig halAreaConfig : config.getAreaConfigs()) { 1174 if (!isPropIdAreaIdReadable(config, halAreaConfig.getAccess())) { 1175 Slogf.w(CarLog.TAG_HAL, "Cannot subscribe to " 1176 + toPropertyIdString(config.getPropId()) + " at areaId " 1177 + toAreaIdString(config.getPropId(), halAreaConfig.getAreaId()) 1178 + " the property's access mode does not contain READ"); 1179 return false; 1180 } 1181 } 1182 return true; 1183 } 1184 1185 /** 1186 * Sets a passed propertyId+areaId from the shell command. 1187 * 1188 * @param propertyId Property ID 1189 * @param areaId Area ID 1190 * @param data Comma-separated value. 1191 */ setPropertyFromCommand(int propertyId, int areaId, String data, IndentingPrintWriter writer)1192 public void setPropertyFromCommand(int propertyId, int areaId, String data, 1193 IndentingPrintWriter writer) throws IllegalArgumentException, ServiceSpecificException { 1194 long timestampNanos = SystemClock.elapsedRealtimeNanos(); 1195 HalPropValue halPropValue = createPropValueForInjecting(mPropValueBuilder, propertyId, 1196 areaId, List.of(data.split(DATA_DELIMITER)), timestampNanos); 1197 if (halPropValue == null) { 1198 throw new IllegalArgumentException( 1199 "Unsupported property type: propertyId=" + toPropertyIdString(propertyId) 1200 + ", areaId=" + toAreaIdString(propertyId, areaId)); 1201 } 1202 set(halPropValue); 1203 } 1204 1205 private final ArraySet<HalServiceBase> mServicesToDispatch = new ArraySet<>(); 1206 1207 @Override onPropertyEvent(ArrayList<HalPropValue> propValues)1208 public void onPropertyEvent(ArrayList<HalPropValue> propValues) { 1209 mHandler.post(() -> handleOnPropertyEvent(propValues)); 1210 } 1211 1212 @Override onPropertySetError(ArrayList<VehiclePropError> errors)1213 public void onPropertySetError(ArrayList<VehiclePropError> errors) { 1214 mHandler.post(() -> handleOnPropertySetError(errors)); 1215 } 1216 1217 @Override 1218 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)1219 public void dump(IndentingPrintWriter writer) { 1220 synchronized (mLock) { 1221 writer.println("**dump HAL services**"); 1222 for (int i = 0; i < mAllServices.size(); i++) { 1223 mAllServices.get(i).dump(writer); 1224 } 1225 // Dump all VHAL property configure. 1226 dumpPropertyConfigs(writer, -1); 1227 writer.printf("**All Events, now ns:%d**\n", 1228 SystemClock.elapsedRealtimeNanos()); 1229 for (int i = 0; i < mEventLog.size(); i++) { 1230 VehiclePropertyEventInfo info = mEventLog.valueAt(i); 1231 writer.printf("event count:%d, lastEvent: ", info.mEventCount); 1232 dumpPropValue(writer, info.mLastEvent); 1233 } 1234 writer.println("**Property handlers**"); 1235 for (int i = 0; i < mPropertyHandlers.size(); i++) { 1236 int propId = mPropertyHandlers.keyAt(i); 1237 HalServiceBase service = mPropertyHandlers.valueAt(i); 1238 writer.printf("Property Id: %d // 0x%x name: %s, service: %s\n", propId, propId, 1239 VehiclePropertyIds.toString(propId), service); 1240 } 1241 } 1242 } 1243 1244 /** 1245 * Dumps or debug VHAL. 1246 */ 1247 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpVhal(ParcelFileDescriptor fd, List<String> options)1248 public void dumpVhal(ParcelFileDescriptor fd, List<String> options) throws RemoteException { 1249 mVehicleStub.dump(fd.getFileDescriptor(), options); 1250 } 1251 1252 /** 1253 * Dumps the list of HALs. 1254 */ dumpListHals(PrintWriter writer)1255 public void dumpListHals(PrintWriter writer) { 1256 synchronized (mLock) { 1257 for (int i = 0; i < mAllServices.size(); i++) { 1258 writer.println(mAllServices.get(i).getClass().getName()); 1259 } 1260 } 1261 } 1262 1263 /** 1264 * Dumps the given HALs. 1265 */ dumpSpecificHals(PrintWriter writer, String... halNames)1266 public void dumpSpecificHals(PrintWriter writer, String... halNames) { 1267 synchronized (mLock) { 1268 ArrayMap<String, HalServiceBase> byName = new ArrayMap<>(); 1269 for (int index = 0; index < mAllServices.size(); index++) { 1270 HalServiceBase halService = mAllServices.get(index); 1271 byName.put(halService.getClass().getSimpleName(), halService); 1272 } 1273 for (String halName : halNames) { 1274 HalServiceBase service = byName.get(halName); 1275 if (service == null) { 1276 writer.printf("No HAL named %s. Valid options are: %s\n", 1277 halName, byName.keySet()); 1278 continue; 1279 } 1280 service.dump(writer); 1281 } 1282 } 1283 } 1284 1285 /** 1286 * Dumps vehicle property values. 1287 * 1288 * @param propertyId property id, dump all properties' value if it is {@code -1}. 1289 * @param areaId areaId of the property, dump the property for all areaIds in the config 1290 * if it is {@code -1} 1291 */ dumpPropertyValueByCommand(PrintWriter writer, int propertyId, int areaId)1292 public void dumpPropertyValueByCommand(PrintWriter writer, int propertyId, int areaId) { 1293 if (propertyId == -1) { 1294 writer.println("**All property values**"); 1295 synchronized (mLock) { 1296 for (int i = 0; i < mAllProperties.size(); i++) { 1297 HalPropConfig config = mAllProperties.valueAt(i); 1298 dumpPropertyValueByConfig(writer, config); 1299 } 1300 } 1301 } else if (areaId == -1) { 1302 synchronized (mLock) { 1303 HalPropConfig config = mAllProperties.get(propertyId); 1304 if (config == null) { 1305 writer.printf("Property: %s not supported by HAL\n", 1306 toPropertyIdString(propertyId)); 1307 return; 1308 } 1309 dumpPropertyValueByConfig(writer, config); 1310 } 1311 } else { 1312 try { 1313 HalPropValue value = get(propertyId, areaId); 1314 dumpPropValue(writer, value); 1315 } catch (RuntimeException e) { 1316 writer.printf("Cannot get property value for property: %s in areaId: %s.\n", 1317 toPropertyIdString(propertyId), toAreaIdString(propertyId, areaId)); 1318 } 1319 } 1320 } 1321 1322 /** 1323 * Gets all property configs from VHAL. 1324 */ getAllPropConfigs()1325 public HalPropConfig[] getAllPropConfigs() throws RemoteException, ServiceSpecificException { 1326 return mVehicleStub.getAllPropConfigs(); 1327 } 1328 1329 /** 1330 * Gets the property config for a property, returns {@code null} if not supported. 1331 */ getPropConfig(int propId)1332 public @Nullable HalPropConfig getPropConfig(int propId) { 1333 synchronized (mLock) { 1334 return mAllProperties.get(propId); 1335 } 1336 } 1337 1338 /** 1339 * Checks whether we are connected to AIDL VHAL: {@code true} or HIDL VHAL: {@code false}. 1340 */ isAidlVhal()1341 public boolean isAidlVhal() { 1342 return mVehicleStub.isAidlVhal(); 1343 } 1344 1345 /** 1346 * Checks if fake VHAL mode is enabled. 1347 * 1348 * @return {@code true} if car service is connected to FakeVehicleStub. 1349 */ isFakeModeEnabled()1350 public boolean isFakeModeEnabled() { 1351 return mVehicleStub.isFakeModeEnabled(); 1352 } 1353 dumpPropertyValueByConfig(PrintWriter writer, HalPropConfig config)1354 private void dumpPropertyValueByConfig(PrintWriter writer, HalPropConfig config) { 1355 int propertyId = config.getPropId(); 1356 HalAreaConfig[] areaConfigs = config.getAreaConfigs(); 1357 if (areaConfigs == null || areaConfigs.length == 0) { 1358 try { 1359 HalPropValue value = get(config.getPropId()); 1360 dumpPropValue(writer, value); 1361 } catch (RuntimeException e) { 1362 writer.printf("Can not get property value for property: %s, areaId: %s\n", 1363 toPropertyIdString(propertyId), toAreaIdString(propertyId, /*areaId=*/0)); 1364 } 1365 } else { 1366 for (HalAreaConfig areaConfig : areaConfigs) { 1367 int areaId = areaConfig.getAreaId(); 1368 try { 1369 HalPropValue value = get(propertyId, areaId); 1370 dumpPropValue(writer, value); 1371 } catch (RuntimeException e) { 1372 writer.printf( 1373 "Can not get property value for property: %s in areaId: %s\n", 1374 toPropertyIdString(propertyId), toAreaIdString(propertyId, areaId)); 1375 } 1376 } 1377 } 1378 } 1379 1380 /** 1381 * Dump VHAL property configs. 1382 * Dump all properties if {@code propertyId} is equal to {@code -1}. 1383 * 1384 * @param propertyId the property ID 1385 */ dumpPropertyConfigs(PrintWriter writer, int propertyId)1386 public void dumpPropertyConfigs(PrintWriter writer, int propertyId) { 1387 HalPropConfig[] configs; 1388 synchronized (mLock) { 1389 configs = new HalPropConfig[mAllProperties.size()]; 1390 for (int i = 0; i < mAllProperties.size(); i++) { 1391 configs[i] = mAllProperties.valueAt(i); 1392 } 1393 } 1394 1395 if (propertyId == -1) { 1396 writer.println("**All properties**"); 1397 for (HalPropConfig config : configs) { 1398 dumpPropertyConfigsHelp(writer, config); 1399 } 1400 return; 1401 } 1402 for (HalPropConfig config : configs) { 1403 if (config.getPropId() == propertyId) { 1404 dumpPropertyConfigsHelp(writer, config); 1405 return; 1406 } 1407 } 1408 } 1409 1410 /** Dumps VehiclePropertyConfigs */ dumpPropertyConfigsHelp(PrintWriter writer, HalPropConfig config)1411 private static void dumpPropertyConfigsHelp(PrintWriter writer, HalPropConfig config) { 1412 int propertyId = config.getPropId(); 1413 writer.printf( 1414 "Property:%s, group:%s, areaType:%s, valueType:%s,\n access:%s, changeMode:%s, " 1415 + "configArray:%s, minSampleRateHz:%f, maxSampleRateHz:%f\n", 1416 toPropertyIdString(propertyId), toGroupString(propertyId), 1417 toAreaTypeString(propertyId), toValueTypeString(propertyId), 1418 toAccessString(config.getAccess()), toChangeModeString(config.getChangeMode()), 1419 Arrays.toString(config.getConfigArray()), config.getMinSampleRate(), 1420 config.getMaxSampleRate()); 1421 if (config.getAreaConfigs() == null) { 1422 return; 1423 } 1424 for (HalAreaConfig area : config.getAreaConfigs()) { 1425 writer.printf(" areaId:%s, access:%s, f min:%f, f max:%f, i min:%d, i max:%d," 1426 + " i64 min:%d, i64 max:%d\n", toAreaIdString(propertyId, 1427 area.getAreaId()), toAccessString(area.getAccess()), 1428 area.getMinFloatValue(), area.getMaxFloatValue(), area.getMinInt32Value(), 1429 area.getMaxInt32Value(), area.getMinInt64Value(), area.getMaxInt64Value()); 1430 } 1431 } 1432 1433 /** 1434 * Inject a VHAL event 1435 * 1436 * @param propertyId the property ID as defined in the HAL 1437 * @param areaId the area ID that this event services 1438 * @param value the data value of the event 1439 * @param delayTimeSeconds add a certain duration to event timestamp 1440 */ injectVhalEvent(int propertyId, int areaId, String value, int delayTimeSeconds)1441 public void injectVhalEvent(int propertyId, int areaId, String value, int delayTimeSeconds) 1442 throws NumberFormatException { 1443 long timestampNanos = SystemClock.elapsedRealtimeNanos() + TimeUnit.SECONDS.toNanos( 1444 delayTimeSeconds); 1445 HalPropValue v = createPropValueForInjecting(mPropValueBuilder, propertyId, areaId, 1446 Arrays.asList(value.split(DATA_DELIMITER)), timestampNanos); 1447 if (v == null) { 1448 return; 1449 } 1450 mHandler.post(() -> handleOnPropertyEvent(Lists.newArrayList(v))); 1451 } 1452 1453 /** 1454 * Injects continuous VHAL events. 1455 * 1456 * @param property the Vehicle property Id as defined in the HAL 1457 * @param zone the zone that this event services 1458 * @param value the data value of the event 1459 * @param sampleRate the sample rate for events in Hz 1460 * @param timeDurationInSec the duration for injecting events in seconds 1461 */ injectContinuousVhalEvent(int property, int zone, String value, float sampleRate, long timeDurationInSec)1462 public void injectContinuousVhalEvent(int property, int zone, String value, 1463 float sampleRate, long timeDurationInSec) { 1464 1465 HalPropValue v = createPropValueForInjecting(mPropValueBuilder, property, zone, 1466 new ArrayList<>(Arrays.asList(value.split(DATA_DELIMITER))), 0); 1467 if (v == null) { 1468 return; 1469 } 1470 // rate in Hz 1471 if (sampleRate <= 0) { 1472 Slogf.e(CarLog.TAG_HAL, "Inject events at an invalid sample rate: " + sampleRate); 1473 return; 1474 } 1475 long period = (long) (1000 / sampleRate); 1476 long stopTime = timeDurationInSec * 1000 + SystemClock.elapsedRealtime(); 1477 Timer timer = new Timer(); 1478 timer.schedule(new TimerTask() { 1479 @Override 1480 public void run() { 1481 if (stopTime < SystemClock.elapsedRealtime()) { 1482 timer.cancel(); 1483 timer.purge(); 1484 } else { 1485 // Avoid the fake events be covered by real Event 1486 long timestamp = SystemClock.elapsedRealtimeNanos() 1487 + TimeUnit.SECONDS.toNanos(timeDurationInSec); 1488 HalPropValue v = createPropValueForInjecting(mPropValueBuilder, property, zone, 1489 new ArrayList<>(Arrays.asList(value.split(DATA_DELIMITER))), timestamp); 1490 mHandler.post(() -> handleOnPropertyEvent(Lists.newArrayList(v))); 1491 } 1492 } 1493 }, /* delay= */0, period); 1494 } 1495 1496 // Returns null if the property type is unsupported. 1497 @Nullable createPropValueForInjecting(HalPropValueBuilder builder, int propId, int zoneId, List<String> dataList, long timestamp)1498 private static HalPropValue createPropValueForInjecting(HalPropValueBuilder builder, 1499 int propId, int zoneId, List<String> dataList, long timestamp) { 1500 int propertyType = propId & VehiclePropertyType.MASK; 1501 // Values can be comma separated list 1502 switch (propertyType) { 1503 case VehiclePropertyType.BOOLEAN: 1504 boolean boolValue = Boolean.parseBoolean(dataList.get(0)); 1505 return builder.build(propId, zoneId, timestamp, VehiclePropertyStatus.AVAILABLE, 1506 boolValue ? 1 : 0); 1507 case VehiclePropertyType.INT64: 1508 case VehiclePropertyType.INT64_VEC: 1509 long[] longValues = new long[dataList.size()]; 1510 for (int i = 0; i < dataList.size(); i++) { 1511 longValues[i] = Long.decode(dataList.get(i)); 1512 } 1513 return builder.build(propId, zoneId, timestamp, VehiclePropertyStatus.AVAILABLE, 1514 longValues); 1515 case VehiclePropertyType.INT32: 1516 case VehiclePropertyType.INT32_VEC: 1517 int[] intValues = new int[dataList.size()]; 1518 for (int i = 0; i < dataList.size(); i++) { 1519 intValues[i] = Integer.decode(dataList.get(i)); 1520 } 1521 return builder.build(propId, zoneId, timestamp, VehiclePropertyStatus.AVAILABLE, 1522 intValues); 1523 case VehiclePropertyType.FLOAT: 1524 case VehiclePropertyType.FLOAT_VEC: 1525 float[] floatValues = new float[dataList.size()]; 1526 for (int i = 0; i < dataList.size(); i++) { 1527 floatValues[i] = Float.parseFloat(dataList.get(i)); 1528 } 1529 return builder.build(propId, zoneId, timestamp, VehiclePropertyStatus.AVAILABLE, 1530 floatValues); 1531 default: 1532 Slogf.e(CarLog.TAG_HAL, "Property type unsupported:" + propertyType); 1533 return null; 1534 } 1535 } 1536 1537 private static class VehiclePropertyEventInfo { 1538 private int mEventCount; 1539 private HalPropValue mLastEvent; 1540 VehiclePropertyEventInfo(HalPropValue event)1541 private VehiclePropertyEventInfo(HalPropValue event) { 1542 mEventCount = 1; 1543 mLastEvent = event; 1544 } 1545 addNewEvent(HalPropValue event)1546 private void addNewEvent(HalPropValue event) { 1547 mEventCount++; 1548 mLastEvent = event; 1549 } 1550 } 1551 1552 final class HalPropValueSetter { 1553 final int mPropId; 1554 final int mAreaId; 1555 HalPropValueSetter(int propId, int areaId)1556 private HalPropValueSetter(int propId, int areaId) { 1557 mPropId = propId; 1558 mAreaId = areaId; 1559 } 1560 1561 /** 1562 * Set the property to the given value. 1563 * 1564 * @throws IllegalArgumentException if argument is invalid 1565 * @throws ServiceSpecificException if VHAL returns error 1566 */ to(boolean value)1567 void to(boolean value) throws IllegalArgumentException, ServiceSpecificException { 1568 to(value ? 1 : 0); 1569 } 1570 1571 /** 1572 * Set the property to the given value. 1573 * 1574 * @throws IllegalArgumentException if argument is invalid 1575 * @throws ServiceSpecificException if VHAL returns error 1576 */ to(int value)1577 void to(int value) throws IllegalArgumentException, ServiceSpecificException { 1578 HalPropValue propValue = mPropValueBuilder.build(mPropId, mAreaId, value); 1579 submit(propValue); 1580 } 1581 1582 /** 1583 * Set the property to the given values. 1584 * 1585 * @throws IllegalArgumentException if argument is invalid 1586 * @throws ServiceSpecificException if VHAL returns error 1587 */ to(int[] values)1588 void to(int[] values) throws IllegalArgumentException, ServiceSpecificException { 1589 HalPropValue propValue = mPropValueBuilder.build(mPropId, mAreaId, values); 1590 submit(propValue); 1591 } 1592 1593 /** 1594 * Set the property to the given values. 1595 * 1596 * @throws IllegalArgumentException if argument is invalid 1597 * @throws ServiceSpecificException if VHAL returns error 1598 */ to(Collection<Integer> values)1599 void to(Collection<Integer> values) 1600 throws IllegalArgumentException, ServiceSpecificException { 1601 int[] intValues = new int[values.size()]; 1602 int i = 0; 1603 for (int value : values) { 1604 intValues[i] = value; 1605 i++; 1606 } 1607 HalPropValue propValue = mPropValueBuilder.build(mPropId, mAreaId, intValues); 1608 submit(propValue); 1609 } 1610 submit(HalPropValue propValue)1611 void submit(HalPropValue propValue) 1612 throws IllegalArgumentException, ServiceSpecificException { 1613 if (DBG) { 1614 Slogf.d(CarLog.TAG_HAL, "set - " + propValue); 1615 } 1616 setValueWithRetry(propValue); 1617 } 1618 } 1619 dumpPropValue(PrintWriter writer, HalPropValue value)1620 private static void dumpPropValue(PrintWriter writer, HalPropValue value) { 1621 writer.println(value); 1622 } 1623 1624 interface RetriableAction { run(HalPropValue requestValue)1625 @Nullable HalPropValue run(HalPropValue requestValue) 1626 throws ServiceSpecificException, RemoteException; 1627 } 1628 invokeRetriable(RetriableAction action, String operation, HalPropValue requestValue, long maxDurationForRetryMs, long sleepBetweenRetryMs, int maxRetries)1629 private static HalPropValue invokeRetriable(RetriableAction action, 1630 String operation, HalPropValue requestValue, long maxDurationForRetryMs, 1631 long sleepBetweenRetryMs, int maxRetries) 1632 throws ServiceSpecificException, IllegalArgumentException { 1633 Retrier retrier = new Retrier(action, operation, requestValue, maxDurationForRetryMs, 1634 sleepBetweenRetryMs, maxRetries); 1635 HalPropValue result = retrier.invokeAction(); 1636 if (DBG) { 1637 Slogf.d(CarLog.TAG_HAL, 1638 "Invoked retriable action for %s - RequestValue: %s - ResultValue: %s, for " 1639 + "retrier: %s", 1640 operation, requestValue, result, retrier); 1641 } 1642 return result; 1643 } 1644 cloneState(PairSparseArray<RateInfo> state)1645 private PairSparseArray<RateInfo> cloneState(PairSparseArray<RateInfo> state) { 1646 PairSparseArray<RateInfo> cloned = new PairSparseArray<>(); 1647 for (int i = 0; i < state.size(); i++) { 1648 int[] keyPair = state.keyPairAt(i); 1649 cloned.put(keyPair[0], keyPair[1], state.valueAt(i)); 1650 } 1651 return cloned; 1652 } 1653 isStaticProperty(HalPropConfig config)1654 private static boolean isStaticProperty(HalPropConfig config) { 1655 return config.getChangeMode() == VehiclePropertyChangeMode.STATIC; 1656 } 1657 1658 private static final class Retrier { 1659 private final RetriableAction mAction; 1660 private final String mOperation; 1661 private final HalPropValue mRequestValue; 1662 private final long mMaxDurationForRetryMs; 1663 private final long mSleepBetweenRetryMs; 1664 private final int mMaxRetries; 1665 private final long mStartTime; 1666 private int mRetryCount = 0; 1667 Retrier(RetriableAction action, String operation, HalPropValue requestValue, long maxDurationForRetryMs, long sleepBetweenRetryMs, int maxRetries)1668 Retrier(RetriableAction action, 1669 String operation, HalPropValue requestValue, long maxDurationForRetryMs, 1670 long sleepBetweenRetryMs, int maxRetries) { 1671 mAction = action; 1672 mOperation = operation; 1673 mRequestValue = requestValue; 1674 mMaxDurationForRetryMs = maxDurationForRetryMs; 1675 mSleepBetweenRetryMs = sleepBetweenRetryMs; 1676 mMaxRetries = maxRetries; 1677 mStartTime = uptimeMillis(); 1678 } 1679 invokeAction()1680 HalPropValue invokeAction() 1681 throws ServiceSpecificException, IllegalArgumentException { 1682 mRetryCount++; 1683 1684 try { 1685 return mAction.run(mRequestValue); 1686 } catch (ServiceSpecificException e) { 1687 switch (e.errorCode) { 1688 case StatusCode.INVALID_ARG: 1689 throw new IllegalArgumentException(errorMessage(mOperation, mRequestValue, 1690 e.toString())); 1691 case StatusCode.TRY_AGAIN: 1692 return sleepAndTryAgain(e); 1693 default: 1694 throw e; 1695 } 1696 } catch (RemoteException e) { 1697 return sleepAndTryAgain(e); 1698 } 1699 } 1700 toString()1701 public String toString() { 1702 return "Retrier{" 1703 + ", Operation=" + mOperation 1704 + ", RequestValue=" + mRequestValue 1705 + ", MaxDurationForRetryMs=" + mMaxDurationForRetryMs 1706 + ", SleepBetweenRetriesMs=" + mSleepBetweenRetryMs 1707 + ", MaxRetries=" + mMaxDurationForRetryMs 1708 + ", StartTime=" + mStartTime 1709 + "}"; 1710 } 1711 sleepAndTryAgain(Exception e)1712 private HalPropValue sleepAndTryAgain(Exception e) 1713 throws ServiceSpecificException, IllegalArgumentException { 1714 Slogf.d(CarLog.TAG_HAL, "trying the request: " 1715 + toPropertyIdString(mRequestValue.getPropId()) + ", " 1716 + toAreaIdString(mRequestValue.getPropId(), mRequestValue.getAreaId()) 1717 + " again..."); 1718 try { 1719 Thread.sleep(mSleepBetweenRetryMs); 1720 } catch (InterruptedException interruptedException) { 1721 Thread.currentThread().interrupt(); 1722 Slogf.w(CarLog.TAG_HAL, "Thread was interrupted while waiting for vehicle HAL.", 1723 interruptedException); 1724 throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR, 1725 errorMessage(mOperation, mRequestValue, interruptedException.toString())); 1726 } 1727 1728 if (mMaxRetries != 0) { 1729 // If mMaxRetries is specified, check the retry count. 1730 if (mMaxRetries == mRetryCount) { 1731 throw new ServiceSpecificException(StatusCode.TRY_AGAIN, 1732 errorMessage(mOperation, mRequestValue, 1733 "cannot get property after " + mRetryCount + " retires, " 1734 + "last exception: " + e)); 1735 } 1736 } else if ((uptimeMillis() - mStartTime) >= mMaxDurationForRetryMs) { 1737 // Otherwise, check whether we have reached timeout. 1738 throw new ServiceSpecificException(StatusCode.TRY_AGAIN, 1739 errorMessage(mOperation, mRequestValue, 1740 "cannot get property within " + mMaxDurationForRetryMs 1741 + "ms, last exception: " + e)); 1742 } 1743 return invokeAction(); 1744 } 1745 } 1746 1747 1748 /** 1749 * Queries HalPropValue with list of GetVehicleHalRequest objects. 1750 * 1751 * <p>This method gets the HalPropValue using async methods. 1752 */ getAsync(List<VehicleStub.AsyncGetSetRequest> getVehicleStubAsyncRequests, VehicleStub.VehicleStubCallbackInterface getVehicleStubAsyncCallback)1753 public void getAsync(List<VehicleStub.AsyncGetSetRequest> getVehicleStubAsyncRequests, 1754 VehicleStub.VehicleStubCallbackInterface getVehicleStubAsyncCallback) { 1755 mVehicleStub.getAsync(getVehicleStubAsyncRequests, getVehicleStubAsyncCallback); 1756 } 1757 1758 /** 1759 * Sets vehicle property value asynchronously. 1760 */ setAsync(List<VehicleStub.AsyncGetSetRequest> setVehicleStubAsyncRequests, VehicleStub.VehicleStubCallbackInterface setVehicleStubAsyncCallback)1761 public void setAsync(List<VehicleStub.AsyncGetSetRequest> setVehicleStubAsyncRequests, 1762 VehicleStub.VehicleStubCallbackInterface setVehicleStubAsyncCallback) { 1763 mVehicleStub.setAsync(setVehicleStubAsyncRequests, setVehicleStubAsyncCallback); 1764 } 1765 1766 /** 1767 * Cancels all the on-going async requests with the given request IDs. 1768 */ cancelRequests(List<Integer> vehicleStubRequestIds)1769 public void cancelRequests(List<Integer> vehicleStubRequestIds) { 1770 mVehicleStub.cancelRequests(vehicleStubRequestIds); 1771 } 1772 } 1773