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