1 /* 2 * Copyright (C) 2021 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; 18 19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE; 20 import static com.android.car.internal.property.CarPropertyErrorCodes.createFromVhalStatusCode; 21 22 import android.annotation.Nullable; 23 import android.car.builtin.os.ServiceManagerHelper; 24 import android.car.builtin.os.TraceHelper; 25 import android.car.builtin.util.Slogf; 26 import android.car.util.concurrent.AndroidFuture; 27 import android.hardware.automotive.vehicle.GetValueRequest; 28 import android.hardware.automotive.vehicle.GetValueRequests; 29 import android.hardware.automotive.vehicle.GetValueResult; 30 import android.hardware.automotive.vehicle.GetValueResults; 31 import android.hardware.automotive.vehicle.IVehicle; 32 import android.hardware.automotive.vehicle.IVehicleCallback; 33 import android.hardware.automotive.vehicle.MinMaxSupportedValueResult; 34 import android.hardware.automotive.vehicle.MinMaxSupportedValueResults; 35 import android.hardware.automotive.vehicle.RawPropValues; 36 import android.hardware.automotive.vehicle.SetValueRequest; 37 import android.hardware.automotive.vehicle.SetValueRequests; 38 import android.hardware.automotive.vehicle.SetValueResult; 39 import android.hardware.automotive.vehicle.SetValueResults; 40 import android.hardware.automotive.vehicle.StatusCode; 41 import android.hardware.automotive.vehicle.SubscribeOptions; 42 import android.hardware.automotive.vehicle.SupportedValuesListResult; 43 import android.hardware.automotive.vehicle.SupportedValuesListResults; 44 import android.hardware.automotive.vehicle.VehiclePropConfig; 45 import android.hardware.automotive.vehicle.VehiclePropConfigs; 46 import android.hardware.automotive.vehicle.VehiclePropError; 47 import android.hardware.automotive.vehicle.VehiclePropErrors; 48 import android.hardware.automotive.vehicle.VehiclePropValue; 49 import android.hardware.automotive.vehicle.VehiclePropValues; 50 import android.os.Handler; 51 import android.os.HandlerThread; 52 import android.os.Looper; 53 import android.os.RemoteException; 54 import android.os.ServiceSpecificException; 55 import android.os.Trace; 56 import android.util.ArrayMap; 57 import android.util.ArraySet; 58 import android.util.LongSparseArray; 59 60 import com.android.car.hal.AidlHalPropConfig; 61 import com.android.car.hal.HalAreaConfig; 62 import com.android.car.hal.HalPropConfig; 63 import com.android.car.hal.HalPropValue; 64 import com.android.car.hal.HalPropValueBuilder; 65 import com.android.car.hal.VehicleHalCallback; 66 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 67 import com.android.car.internal.LargeParcelable; 68 import com.android.car.internal.LongPendingRequestPool; 69 import com.android.car.internal.LongPendingRequestPool.TimeoutCallback; 70 import com.android.car.internal.LongRequestIdWithTimeout; 71 import com.android.car.internal.property.CarPropertyErrorCodes; 72 import com.android.car.internal.property.PropIdAreaId; 73 import com.android.car.logging.HistogramFactoryInterface; 74 import com.android.car.logging.SystemHistogramFactory; 75 import com.android.internal.annotations.GuardedBy; 76 import com.android.internal.annotations.VisibleForTesting; 77 import com.android.modules.expresslog.Histogram; 78 79 import java.io.FileDescriptor; 80 import java.util.ArrayList; 81 import java.util.List; 82 import java.util.Map; 83 import java.util.Set; 84 import java.util.concurrent.ExecutionException; 85 import java.util.concurrent.TimeUnit; 86 import java.util.concurrent.TimeoutException; 87 import java.util.concurrent.atomic.AtomicLong; 88 import java.util.function.Function; 89 90 final class AidlVehicleStub extends VehicleStub { 91 private final Histogram mVehicleHalGetSyncLatencyHistogram; 92 private final Histogram mVehicleHalSetSyncLatencyHistogram; 93 94 private static final String AIDL_VHAL_SERVICE = 95 "android.hardware.automotive.vehicle.IVehicle/default"; 96 // default timeout: 10s 97 private static final long DEFAULT_TIMEOUT_MS = 10_000; 98 99 private static final int MIN_SUPPORTED_VALUES_IMPLEMENTED_VHAL_VERSION = 4; 100 101 private static final String TAG = CarLog.tagFor(AidlVehicleStub.class); 102 private static final long TRACE_TAG = TraceHelper.TRACE_TAG_CAR_SERVICE; 103 104 private final IVehicle mAidlVehicle; 105 private final HalPropValueBuilder mPropValueBuilder; 106 private final GetSetValuesCallback mGetSetValuesCallback; 107 private final HandlerThread mHandlerThread; 108 private final Handler mHandler; 109 private final AtomicLong mRequestId = new AtomicLong(0); 110 private final Object mLock = new Object(); 111 // PendingSyncRequestPool is thread-safe. 112 private final PendingSyncRequestPool<GetValueResult> mPendingSyncGetValueRequestPool = 113 new PendingSyncRequestPool<>(); 114 private final PendingSyncRequestPool<SetValueResult> mPendingSyncSetValueRequestPool = 115 new PendingSyncRequestPool<>(); 116 // PendingAsyncRequestPool is thread-safe. 117 private final PendingAsyncRequestPool mPendingAsyncRequestPool; 118 119 // This might be modifed during tests. 120 private long mSyncOpTimeoutInMs = DEFAULT_TIMEOUT_MS; 121 122 private static class AsyncRequestInfo implements LongRequestIdWithTimeout { 123 private final int mServiceRequestId; 124 private final VehicleStubCallbackInterface mClientCallback; 125 private final long mTimeoutUptimeMs; 126 private final long mVhalRequestId; 127 AsyncRequestInfo( long vhalRequestId, int serviceRequestId, VehicleStubCallbackInterface clientCallback, long timeoutUptimeMs)128 private AsyncRequestInfo( 129 long vhalRequestId, 130 int serviceRequestId, 131 VehicleStubCallbackInterface clientCallback, 132 long timeoutUptimeMs) { 133 mVhalRequestId = vhalRequestId; 134 mServiceRequestId = serviceRequestId; 135 mClientCallback = clientCallback; 136 mTimeoutUptimeMs = timeoutUptimeMs; 137 } 138 139 @Override getRequestId()140 public long getRequestId() { 141 return mVhalRequestId; 142 } 143 144 @Override getTimeoutUptimeMs()145 public long getTimeoutUptimeMs() { 146 return mTimeoutUptimeMs; 147 } 148 getServiceRequestId()149 public int getServiceRequestId() { 150 return mServiceRequestId; 151 } 152 getClientCallback()153 public VehicleStubCallbackInterface getClientCallback() { 154 return mClientCallback; 155 } 156 } 157 AidlVehicleStub()158 AidlVehicleStub() { 159 this(getAidlVehicle()); 160 } 161 162 @VisibleForTesting AidlVehicleStub(IVehicle aidlVehicle)163 AidlVehicleStub(IVehicle aidlVehicle) { 164 this(aidlVehicle, 165 CarServiceUtils.getHandlerThread(AidlVehicleStub.class.getSimpleName()), 166 new SystemHistogramFactory()); 167 } 168 169 @VisibleForTesting AidlVehicleStub(IVehicle aidlVehicle, HandlerThread handlerThread, HistogramFactoryInterface histogramFactory)170 AidlVehicleStub(IVehicle aidlVehicle, HandlerThread handlerThread, 171 HistogramFactoryInterface histogramFactory) { 172 mAidlVehicle = aidlVehicle; 173 mPropValueBuilder = new HalPropValueBuilder(/*isAidl=*/true); 174 mHandlerThread = handlerThread; 175 mHandler = new Handler(mHandlerThread.getLooper()); 176 mGetSetValuesCallback = new GetSetValuesCallback(); 177 mPendingAsyncRequestPool = new PendingAsyncRequestPool(mHandler.getLooper()); 178 mVehicleHalGetSyncLatencyHistogram = histogramFactory.newScaledRangeHistogram( 179 "automotive_os.value_sync_hal_get_property_latency", /* binCount= */ 20, 180 /* minValue= */ 0, /* firstBinWidth= */ 2, /* scaleFactor= */ 1.5f); 181 mVehicleHalSetSyncLatencyHistogram = histogramFactory.newScaledRangeHistogram( 182 "automotive_os.value_sync_hal_set_property_latency", /* binCount= */ 20, 183 /* minValue= */ 0, /* firstBinWidth= */ 2, /* scaleFactor= */ 1.5f); 184 } 185 186 /** 187 * Sets the timeout for getValue/setValue requests in milliseconds. 188 */ 189 @VisibleForTesting setSyncOpTimeoutInMs(long timeoutMs)190 void setSyncOpTimeoutInMs(long timeoutMs) { 191 mSyncOpTimeoutInMs = timeoutMs; 192 } 193 194 @VisibleForTesting countPendingRequests()195 int countPendingRequests() { 196 synchronized (mLock) { 197 return mPendingAsyncRequestPool.size() 198 + mPendingSyncGetValueRequestPool.size() 199 + mPendingSyncSetValueRequestPool.size(); 200 } 201 } 202 203 /** 204 * Checks whether we are connected to AIDL VHAL: {@code true} or HIDL VHAL: {@code false}. 205 */ 206 @Override isAidlVhal()207 public boolean isAidlVhal() { 208 return true; 209 } 210 211 /** 212 * Gets a HalPropValueBuilder that could be used to build a HalPropValue. 213 * 214 * @return a builder to build HalPropValue. 215 */ 216 @Override getHalPropValueBuilder()217 public HalPropValueBuilder getHalPropValueBuilder() { 218 return mPropValueBuilder; 219 } 220 221 /** 222 * Returns whether this vehicle stub is connecting to a valid vehicle HAL. 223 * 224 * @return Whether this vehicle stub is connecting to a valid vehicle HAL. 225 */ 226 @Override isValid()227 public boolean isValid() { 228 return mAidlVehicle != null; 229 } 230 231 /** 232 * Gets the interface descriptor for the connecting vehicle HAL. 233 * 234 * @return the interface descriptor. 235 * @throws IllegalStateException If unable to get the descriptor. 236 */ 237 @Override getInterfaceDescriptor()238 public String getInterfaceDescriptor() throws IllegalStateException { 239 try { 240 return mAidlVehicle.asBinder().getInterfaceDescriptor(); 241 } catch (RemoteException e) { 242 throw new IllegalStateException("Unable to get Vehicle HAL interface descriptor", e); 243 } 244 } 245 246 /** 247 * Registers a death recipient that would be called when vehicle HAL died. 248 * 249 * @param recipient A death recipient. 250 * @throws IllegalStateException If unable to register the death recipient. 251 */ 252 @Override linkToDeath(IVehicleDeathRecipient recipient)253 public void linkToDeath(IVehicleDeathRecipient recipient) throws IllegalStateException { 254 try { 255 mAidlVehicle.asBinder().linkToDeath(recipient, /*flag=*/ 0); 256 } catch (RemoteException e) { 257 throw new IllegalStateException("Failed to linkToDeath Vehicle HAL"); 258 } 259 } 260 261 /** 262 * Unlinks a previously linked death recipient. 263 * 264 * @param recipient A previously linked death recipient. 265 */ 266 @Override unlinkToDeath(IVehicleDeathRecipient recipient)267 public void unlinkToDeath(IVehicleDeathRecipient recipient) { 268 mAidlVehicle.asBinder().unlinkToDeath(recipient, /*flag=*/ 0); 269 } 270 271 /** 272 * Gets all property configs. 273 * 274 * @return All the property configs. 275 * @throws RemoteException if the remote operation fails. 276 * @throws ServiceSpecificException if VHAL returns service specific error. 277 */ 278 @Override getAllPropConfigs()279 public HalPropConfig[] getAllPropConfigs() 280 throws RemoteException, ServiceSpecificException { 281 var aidlConfigs = mAidlVehicle.getAllPropConfigs(); 282 VehiclePropConfigs propConfigs = (VehiclePropConfigs) 283 LargeParcelable.reconstructStableAIDLParcelable( 284 aidlConfigs, aidlConfigs.sharedMemoryFd, VehiclePropConfigs.CREATOR); 285 VehiclePropConfig[] payloads = propConfigs.payloads; 286 int size = payloads.length; 287 HalPropConfig[] configs = new HalPropConfig[size]; 288 for (int i = 0; i < size; i++) { 289 configs[i] = new AidlHalPropConfig(payloads[i]); 290 } 291 return configs; 292 } 293 294 /** 295 * Gets a new {@code SubscriptionClient} that could be used to subscribe/unsubscribe. 296 * 297 * @param callback A callback that could be used to receive events. 298 * @return a {@code SubscriptionClient} that could be used to subscribe/unsubscribe. 299 */ 300 @Override newSubscriptionClient(VehicleHalCallback callback)301 public SubscriptionClient newSubscriptionClient(VehicleHalCallback callback) { 302 return new AidlSubscriptionClient(callback, mPropValueBuilder); 303 } 304 305 /** 306 * Gets a property. 307 * 308 * @param requestedPropValue The property to get. 309 * @return The vehicle property value. 310 * @throws RemoteException if the remote operation fails. 311 * @throws ServiceSpecificException if VHAL returns service specific error. 312 */ 313 @Override 314 @Nullable get(HalPropValue requestedPropValue)315 public HalPropValue get(HalPropValue requestedPropValue) 316 throws RemoteException, ServiceSpecificException { 317 long currentTime = System.currentTimeMillis(); 318 HalPropValue halPropValue = getOrSetSync(requestedPropValue, 319 mPendingSyncGetValueRequestPool, new AsyncGetRequestsHandler(), 320 (result) -> { 321 if (result.status != StatusCode.OK) { 322 throw new ServiceSpecificException(result.status, 323 "failed to get value for " + printPropIdAreaId(requestedPropValue)); 324 } 325 if (result.prop == null) { 326 return null; 327 } 328 return mPropValueBuilder.build(result.prop); 329 }); 330 mVehicleHalGetSyncLatencyHistogram.logSample((float) 331 (System.currentTimeMillis() - currentTime)); 332 return halPropValue; 333 } 334 335 /** 336 * Sets a property. 337 * 338 * @param requestedPropValue The property to set. 339 * @throws RemoteException if the remote operation fails. 340 * @throws ServiceSpecificException if VHAL returns service specific error. 341 */ 342 @Override set(HalPropValue requestedPropValue)343 public void set(HalPropValue requestedPropValue) throws RemoteException, 344 ServiceSpecificException { 345 long currentTime = System.currentTimeMillis(); 346 getOrSetSync(requestedPropValue, mPendingSyncSetValueRequestPool, 347 new AsyncSetRequestsHandler(), 348 (result) -> { 349 if (result.status != StatusCode.OK) { 350 throw new ServiceSpecificException(result.status, 351 "failed to set value for " + printPropIdAreaId(requestedPropValue)); 352 } 353 return null; 354 }); 355 mVehicleHalSetSyncLatencyHistogram.logSample((float) 356 (System.currentTimeMillis() - currentTime)); 357 } 358 359 @Override getAsync(List<AsyncGetSetRequest> getVehicleStubAsyncRequests, VehicleStubCallbackInterface getCallback)360 public void getAsync(List<AsyncGetSetRequest> getVehicleStubAsyncRequests, 361 VehicleStubCallbackInterface getCallback) { 362 getOrSetAsync(getVehicleStubAsyncRequests, getCallback, new AsyncGetRequestsHandler(), 363 new AsyncGetResultsHandler(mPropValueBuilder)); 364 } 365 366 @Override setAsync(List<AsyncGetSetRequest> setVehicleStubAsyncRequests, VehicleStubCallbackInterface setCallback)367 public void setAsync(List<AsyncGetSetRequest> setVehicleStubAsyncRequests, 368 VehicleStubCallbackInterface setCallback) { 369 getOrSetAsync(setVehicleStubAsyncRequests, setCallback, new AsyncSetRequestsHandler(), 370 new AsyncSetResultsHandler()); 371 } 372 373 @Override dump(FileDescriptor fd, List<String> args)374 public void dump(FileDescriptor fd, List<String> args) throws RemoteException { 375 mAidlVehicle.asBinder().dump(fd, args.toArray(new String[args.size()])); 376 } 377 378 // Get all the VHAL request IDs according to the service request IDs and remove them from 379 // pending requests map. 380 @Override cancelRequests(List<Integer> serviceRequestIds)381 public void cancelRequests(List<Integer> serviceRequestIds) { 382 mPendingAsyncRequestPool.cancelRequests(serviceRequestIds); 383 } 384 385 @Override isSupportedValuesImplemented(HalAreaConfig halAreaConfig)386 public boolean isSupportedValuesImplemented(HalAreaConfig halAreaConfig) { 387 // We start supporting dynamic supported values API from V4. 388 int vhalInterfaceVersion; 389 try { 390 vhalInterfaceVersion = mAidlVehicle.getInterfaceVersion(); 391 } catch (RemoteException e) { 392 Slogf.e(TAG, "Failed to get VHAL interface version, default " 393 + "isSupportedValuesImplemented to false", e); 394 return false; 395 } 396 if (vhalInterfaceVersion < MIN_SUPPORTED_VALUES_IMPLEMENTED_VHAL_VERSION) { 397 return false; 398 } 399 return halAreaConfig.getHasSupportedValueInfo() != null; 400 } 401 402 /** 403 * Gets the min/max supported value. 404 * 405 * Caller should only call this if {@link #isSupportedValuesImplemented} is {@code true}. 406 * 407 * If no min/max supported value is specified, return an empty structure. 408 * 409 * @throws ServiceSpecificException if the operation fails. 410 */ 411 @Override getMinMaxSupportedValue( int propertyId, int areaId)412 public MinMaxSupportedRawPropValues getMinMaxSupportedValue( 413 int propertyId, int areaId) throws ServiceSpecificException { 414 var propIdAreaId = new android.hardware.automotive.vehicle.PropIdAreaId(); 415 propIdAreaId.propId = propertyId; 416 propIdAreaId.areaId = areaId; 417 MinMaxSupportedValueResults results; 418 try { 419 // This may throw ServiceSpecificException, we just rethrow it. 420 results = mAidlVehicle.getMinMaxSupportedValue(List.of(propIdAreaId)); 421 } catch (RemoteException e) { 422 throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR, 423 "failed to connect to VHAL: " + e); 424 } catch (ServiceSpecificException e) { 425 throw new ServiceSpecificException(e.errorCode, 426 "VHAL returns non-okay status code: " + e); 427 } 428 var actualResults = (MinMaxSupportedValueResults) 429 LargeParcelable.reconstructStableAIDLParcelable( 430 results, results.sharedMemoryFd, MinMaxSupportedValueResults.CREATOR); 431 MinMaxSupportedValueResult result = actualResults.payloads[0]; 432 if (result.status != StatusCode.OK) { 433 throw new ServiceSpecificException(result.status, 434 "MinMaxSupportedValueResult contains non-okay status code"); 435 } 436 return new MinMaxSupportedRawPropValues(result.minSupportedValue, result.maxSupportedValue); 437 } 438 439 /** 440 * Gets the supported values list. 441 * 442 * Caller should only call this if {@link #isSupportedValuesImplemented} is {@code true}. 443 * 444 * If no supported values list is specified, return {@code null}. 445 * 446 * @throws ServiceSpecificException if the operation fails. 447 */ 448 @Override getSupportedValuesList(int propertyId, int areaId)449 public @Nullable List<RawPropValues> getSupportedValuesList(int propertyId, int areaId) 450 throws ServiceSpecificException { 451 var propIdAreaId = new android.hardware.automotive.vehicle.PropIdAreaId(); 452 propIdAreaId.propId = propertyId; 453 propIdAreaId.areaId = areaId; 454 SupportedValuesListResults results; 455 try { 456 // This may throw ServiceSpecificException, we just rethrow it. 457 results = mAidlVehicle.getSupportedValuesLists(List.of(propIdAreaId)); 458 } catch (RemoteException e) { 459 throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR, 460 "failed to connect to VHAL: " + e); 461 } catch (ServiceSpecificException e) { 462 throw new ServiceSpecificException(e.errorCode, 463 "VHAL returns non-okay status code: " + e); 464 } 465 var actualResults = (SupportedValuesListResults) 466 LargeParcelable.reconstructStableAIDLParcelable( 467 results, results.sharedMemoryFd, SupportedValuesListResults.CREATOR); 468 SupportedValuesListResult result = actualResults.payloads[0]; 469 if (result.status != StatusCode.OK) { 470 throw new ServiceSpecificException(result.status, 471 "SupportedValuesListResult contains non-okay status code"); 472 } 473 return result.supportedValuesList; 474 } 475 476 /** 477 * A thread-safe pending sync request pool. 478 */ 479 private static final class PendingSyncRequestPool<VhalResultType> { 480 private final Object mSyncRequestPoolLock = new Object(); 481 @GuardedBy("mSyncRequestPoolLock") 482 private final LongSparseArray<AndroidFuture<VhalResultType>> 483 mPendingRequestsByVhalRequestId = new LongSparseArray(); 484 addRequest(long vhalRequestId)485 AndroidFuture<VhalResultType> addRequest(long vhalRequestId) { 486 synchronized (mSyncRequestPoolLock) { 487 AndroidFuture<VhalResultType> resultFuture = new AndroidFuture(); 488 mPendingRequestsByVhalRequestId.put(vhalRequestId, resultFuture); 489 return resultFuture; 490 } 491 } 492 finishRequestIfFound(long vhalRequestId)493 @Nullable AndroidFuture<VhalResultType> finishRequestIfFound(long vhalRequestId) { 494 synchronized (mSyncRequestPoolLock) { 495 AndroidFuture<VhalResultType> pendingRequest = 496 mPendingRequestsByVhalRequestId.get(vhalRequestId); 497 mPendingRequestsByVhalRequestId.remove(vhalRequestId); 498 return pendingRequest; 499 } 500 } 501 size()502 int size() { 503 synchronized (mSyncRequestPoolLock) { 504 return mPendingRequestsByVhalRequestId.size(); 505 } 506 } 507 } 508 509 /** 510 * A thread-safe pending async request pool. 511 */ 512 private static final class PendingAsyncRequestPool { 513 private final Object mAsyncRequestPoolLock = new Object(); 514 private final TimeoutCallback mTimeoutCallback = new AsyncRequestTimeoutCallback(); 515 private final Looper mLooper; 516 @GuardedBy("mAsyncRequestPoolLock") 517 private final LongPendingRequestPool<AsyncRequestInfo> mPendingRequestPool; 518 PendingAsyncRequestPool(Looper looper)519 PendingAsyncRequestPool(Looper looper) { 520 mLooper = looper; 521 mPendingRequestPool = new LongPendingRequestPool<>(mLooper, mTimeoutCallback); 522 } 523 524 private class AsyncRequestTimeoutCallback implements TimeoutCallback { 525 @Override onRequestsTimeout(List<Long> vhalRequestIds)526 public void onRequestsTimeout(List<Long> vhalRequestIds) { 527 ArrayMap<VehicleStubCallbackInterface, List<Integer>> serviceRequestIdsByCallback = 528 new ArrayMap<>(); 529 for (int i = 0; i < vhalRequestIds.size(); i++) { 530 long vhalRequestId = vhalRequestIds.get(i); 531 AsyncRequestInfo requestInfo = finishRequestIfFound(vhalRequestId, 532 /* alreadyTimedOut= */ true); 533 if (requestInfo == null) { 534 // We already finished the request or the callback is already dead, ignore. 535 Slogf.w(TAG, "onRequestsTimeout: the request for VHAL request ID: %d is " 536 + "already finished or the callback is already dead, ignore", 537 vhalRequestId); 538 continue; 539 } 540 VehicleStubCallbackInterface getAsyncCallback = requestInfo.getClientCallback(); 541 if (serviceRequestIdsByCallback.get(getAsyncCallback) == null) { 542 serviceRequestIdsByCallback.put(getAsyncCallback, new ArrayList<>()); 543 } 544 serviceRequestIdsByCallback.get(getAsyncCallback).add( 545 requestInfo.getServiceRequestId()); 546 } 547 548 for (int i = 0; i < serviceRequestIdsByCallback.size(); i++) { 549 serviceRequestIdsByCallback.keyAt(i).onRequestsTimeout( 550 serviceRequestIdsByCallback.valueAt(i)); 551 } 552 } 553 } 554 555 addRequests(List<AsyncRequestInfo> requestInfo)556 void addRequests(List<AsyncRequestInfo> requestInfo) { 557 synchronized (mAsyncRequestPoolLock) { 558 mPendingRequestPool.addPendingRequests(requestInfo); 559 } 560 } 561 finishRequestIfFound(long vhalRequestId, boolean alreadyTimedOut)562 @Nullable AsyncRequestInfo finishRequestIfFound(long vhalRequestId, 563 boolean alreadyTimedOut) { 564 synchronized (mAsyncRequestPoolLock) { 565 AsyncRequestInfo requestInfo = mPendingRequestPool.getRequestIfFound(vhalRequestId); 566 mPendingRequestPool.removeRequest(vhalRequestId, alreadyTimedOut); 567 return requestInfo; 568 } 569 } 570 size()571 int size() { 572 synchronized (mAsyncRequestPoolLock) { 573 return mPendingRequestPool.size(); 574 } 575 } 576 contains(long vhalRequestId)577 boolean contains(long vhalRequestId) { 578 synchronized (mAsyncRequestPoolLock) { 579 return mPendingRequestPool.getRequestIfFound(vhalRequestId) != null; 580 } 581 } 582 cancelRequests(List<Integer> serviceRequestIds)583 void cancelRequests(List<Integer> serviceRequestIds) { 584 Set<Integer> serviceRequestIdsSet = new ArraySet<>(serviceRequestIds); 585 List<Long> vhalRequestIdsToCancel = new ArrayList<>(); 586 synchronized (mAsyncRequestPoolLock) { 587 for (int i = 0; i < mPendingRequestPool.size(); i++) { 588 int serviceRequestId = mPendingRequestPool.valueAt(i) 589 .getServiceRequestId(); 590 if (serviceRequestIdsSet.contains(serviceRequestId)) { 591 vhalRequestIdsToCancel.add(mPendingRequestPool.keyAt(i)); 592 } 593 } 594 for (int i = 0; i < vhalRequestIdsToCancel.size(); i++) { 595 long vhalRequestIdToCancel = vhalRequestIdsToCancel.get(i); 596 Slogf.w(TAG, "the request for VHAL request ID: %d is cancelled", 597 vhalRequestIdToCancel); 598 mPendingRequestPool.removeRequest(vhalRequestIdToCancel); 599 } 600 } 601 } 602 removeRequestsForCallback(VehicleStubCallbackInterface callback)603 void removeRequestsForCallback(VehicleStubCallbackInterface callback) { 604 synchronized (mAsyncRequestPoolLock) { 605 List<Long> requestIdsToRemove = new ArrayList<>(); 606 607 for (int i = 0; i < mPendingRequestPool.size(); i++) { 608 if (mPendingRequestPool.valueAt(i).getClientCallback() == callback) { 609 requestIdsToRemove.add(mPendingRequestPool.keyAt(i)); 610 } 611 } 612 613 for (int i = 0; i < requestIdsToRemove.size(); i++) { 614 mPendingRequestPool.removeRequest(requestIdsToRemove.get(i)); 615 } 616 } 617 } 618 } 619 620 /** 621 * An abstract interface for handling async get/set value requests from vehicle stub. 622 */ 623 private abstract static class AsyncRequestsHandler<VhalRequestType, VhalRequestsType> { 624 /** 625 * Preallocsate size array for storing VHAL requests. 626 */ allocateVhalRequestSize(int size)627 abstract void allocateVhalRequestSize(int size); 628 629 /** 630 * Add a vhal request to be sent later. 631 */ addVhalRequest(long vhalRequestId, HalPropValue halPropValue)632 abstract void addVhalRequest(long vhalRequestId, HalPropValue halPropValue); 633 634 /** 635 * Get the list of stored request items. 636 */ getRequestItems()637 abstract VhalRequestType[] getRequestItems(); 638 639 /** 640 * Send the prepared requests to VHAL. 641 */ sendRequestsToVhal(IVehicle iVehicle, GetSetValuesCallback callbackForVhal)642 abstract void sendRequestsToVhal(IVehicle iVehicle, GetSetValuesCallback callbackForVhal) 643 throws RemoteException, ServiceSpecificException; 644 645 /** 646 * Get the request ID for the request. 647 */ getVhalRequestId(VhalRequestType vhalRequest)648 abstract long getVhalRequestId(VhalRequestType vhalRequest); 649 } 650 651 /** 652 * An abstract class to handle async get/set value results from VHAL. 653 */ 654 private abstract static class AsyncResultsHandler<VhalResultType, VehicleStubResultType> { 655 protected Map<VehicleStubCallbackInterface, List<VehicleStubResultType>> mCallbackToResults; 656 657 /** 658 * Add an error result to be sent to vehicleStub through the callback later. 659 */ addErrorResult(VehicleStubCallbackInterface callback, int serviceRequestId, CarPropertyErrorCodes errorCodes)660 abstract void addErrorResult(VehicleStubCallbackInterface callback, int serviceRequestId, 661 CarPropertyErrorCodes errorCodes); 662 /** 663 * Add a VHAL result to be sent to vehicleStub through the callback later. 664 */ addVhalResult(VehicleStubCallbackInterface callback, int serviceRequestId, VhalResultType result)665 abstract void addVhalResult(VehicleStubCallbackInterface callback, int serviceRequestId, 666 VhalResultType result); 667 /** 668 * Send all the stored results to vehicleStub. 669 */ callVehicleStubCallback()670 abstract void callVehicleStubCallback(); 671 672 /** 673 * Get the request ID for the result. 674 */ getVhalRequestId(VhalResultType vhalRequest)675 abstract long getVhalRequestId(VhalResultType vhalRequest); 676 addVehicleStubResult(VehicleStubCallbackInterface callback, VehicleStubResultType vehicleStubResult)677 protected void addVehicleStubResult(VehicleStubCallbackInterface callback, 678 VehicleStubResultType vehicleStubResult) { 679 if (mCallbackToResults.get(callback) == null) { 680 mCallbackToResults.put(callback, new ArrayList<>()); 681 } 682 mCallbackToResults.get(callback).add(vehicleStubResult); 683 } 684 } 685 686 @Nullable getAidlVehicle()687 private static IVehicle getAidlVehicle() { 688 try { 689 return IVehicle.Stub.asInterface( 690 ServiceManagerHelper.waitForDeclaredService(AIDL_VHAL_SERVICE)); 691 } catch (RuntimeException e) { 692 Slogf.w(TAG, "Failed to get \"" + AIDL_VHAL_SERVICE + "\" service", e); 693 } 694 return null; 695 } 696 697 private class AidlSubscriptionClient extends IVehicleCallback.Stub 698 implements SubscriptionClient { 699 private final VehicleHalCallback mCallback; 700 private final HalPropValueBuilder mBuilder; 701 AidlSubscriptionClient(VehicleHalCallback callback, HalPropValueBuilder builder)702 AidlSubscriptionClient(VehicleHalCallback callback, HalPropValueBuilder builder) { 703 mCallback = callback; 704 mBuilder = builder; 705 } 706 707 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) 708 @Override onGetValues(GetValueResults responses)709 public void onGetValues(GetValueResults responses) throws RemoteException { 710 // We use GetSetValuesCallback for getValues and setValues operation. 711 throw new UnsupportedOperationException( 712 "onGetValues should never be called on AidlSubscriptionClient"); 713 } 714 715 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) 716 @Override onSetValues(SetValueResults responses)717 public void onSetValues(SetValueResults responses) throws RemoteException { 718 // We use GetSetValuesCallback for getValues and setValues operation. 719 throw new UnsupportedOperationException( 720 "onSetValues should never be called on AidlSubscriptionClient"); 721 } 722 723 @Override onSupportedValueChange( List<android.hardware.automotive.vehicle.PropIdAreaId> vhalPropIdAreaIds)724 public void onSupportedValueChange( 725 List<android.hardware.automotive.vehicle.PropIdAreaId> vhalPropIdAreaIds) 726 throws RemoteException { 727 mCallback.onSupportedValuesChange(fromVhalPropIdAreaIds(vhalPropIdAreaIds)); 728 } 729 730 @Override onPropertyEvent(VehiclePropValues propValues, int sharedMemoryFileCount)731 public void onPropertyEvent(VehiclePropValues propValues, int sharedMemoryFileCount) 732 throws RemoteException { 733 VehiclePropValues origPropValues = (VehiclePropValues) 734 LargeParcelable.reconstructStableAIDLParcelable( 735 propValues, propValues.sharedMemoryFd, VehiclePropValues.CREATOR); 736 ArrayList<HalPropValue> values = new ArrayList<>(origPropValues.payloads.length); 737 for (VehiclePropValue value : origPropValues.payloads) { 738 values.add(mBuilder.build(value)); 739 } 740 mCallback.onPropertyEvent(values); 741 } 742 743 @Override onPropertySetError(VehiclePropErrors errors)744 public void onPropertySetError(VehiclePropErrors errors) throws RemoteException { 745 VehiclePropErrors origErrors = (VehiclePropErrors) 746 LargeParcelable.reconstructStableAIDLParcelable( 747 errors, errors.sharedMemoryFd, VehiclePropErrors.CREATOR); 748 ArrayList<VehiclePropError> errorList = new ArrayList<>(origErrors.payloads.length); 749 for (VehiclePropError error : origErrors.payloads) { 750 errorList.add(error); 751 } 752 mCallback.onPropertySetError(errorList); 753 } 754 755 @Override subscribe(SubscribeOptions[] options)756 public void subscribe(SubscribeOptions[] options) 757 throws RemoteException, ServiceSpecificException { 758 mAidlVehicle.subscribe(this, options, /* maxSharedMemoryFileCount= */ 2); 759 } 760 761 @Override unsubscribe(int prop)762 public void unsubscribe(int prop) throws RemoteException, ServiceSpecificException { 763 mAidlVehicle.unsubscribe(this, new int[]{prop}); 764 } 765 766 /** 767 * Registers the callback to be called when the min/max supported value or supportd values 768 * list change for the [propId, areaId]s. 769 * 770 * @throws ServiceSpecificException If VHAL returns error or VHAL connection fails. 771 */ 772 @Override registerSupportedValuesChange(List<PropIdAreaId> propIdAreaIds)773 public void registerSupportedValuesChange(List<PropIdAreaId> propIdAreaIds) { 774 try { 775 mAidlVehicle.registerSupportedValueChangeCallback(this, 776 toVhalPropIdAreaIds(propIdAreaIds)); 777 } catch (RemoteException e) { 778 throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR, 779 "failed to connect to VHAL: " + e); 780 } catch (ServiceSpecificException e) { 781 throw new ServiceSpecificException(e.errorCode, 782 "VHAL returns non-okay status code: " + e); 783 } 784 } 785 786 /** 787 * Unregisters the [propId, areaId]s previously registered with 788 * registerSupportedValuesChange. 789 * 790 * Do nothing if the [propId, areaId]s were not previously registered. 791 */ 792 @Override unregisterSupportedValuesChange(List<PropIdAreaId> propIdAreaIds)793 public void unregisterSupportedValuesChange(List<PropIdAreaId> propIdAreaIds) { 794 try { 795 mAidlVehicle.unregisterSupportedValueChangeCallback(this, 796 toVhalPropIdAreaIds(propIdAreaIds)); 797 } catch (RemoteException | ServiceSpecificException e) { 798 Slogf.e(TAG, "Failed to call unregisterSupportedValueChangeCallback to VHAL for " 799 + "propIdAreaIds: " + propIdAreaIds, e); 800 } 801 } 802 803 @Override getInterfaceHash()804 public String getInterfaceHash() { 805 return IVehicleCallback.HASH; 806 } 807 808 @Override getInterfaceVersion()809 public int getInterfaceVersion() { 810 return IVehicleCallback.VERSION; 811 } 812 toVhalPropIdAreaIds( List<PropIdAreaId> propIdAreaIds)813 private static List<android.hardware.automotive.vehicle.PropIdAreaId> toVhalPropIdAreaIds( 814 List<PropIdAreaId> propIdAreaIds) { 815 var vhalPropIdAreaIds = 816 new ArrayList<android.hardware.automotive.vehicle.PropIdAreaId>(); 817 for (int i = 0; i < propIdAreaIds.size(); i++) { 818 var propIdAreaId = propIdAreaIds.get(i); 819 var vhalPropIdAreaId = new android.hardware.automotive.vehicle.PropIdAreaId(); 820 vhalPropIdAreaId.propId = propIdAreaId.propId; 821 vhalPropIdAreaId.areaId = propIdAreaId.areaId; 822 vhalPropIdAreaIds.add(vhalPropIdAreaId); 823 } 824 return vhalPropIdAreaIds; 825 } 826 fromVhalPropIdAreaIds( List<android.hardware.automotive.vehicle.PropIdAreaId> vhalPropIdAreaIds)827 private static List<PropIdAreaId> fromVhalPropIdAreaIds( 828 List<android.hardware.automotive.vehicle.PropIdAreaId> vhalPropIdAreaIds) { 829 var propIdAreaIds = new ArrayList<PropIdAreaId>(); 830 for (int i = 0; i < vhalPropIdAreaIds.size(); i++) { 831 var vhalPropIdAreaId = vhalPropIdAreaIds.get(i); 832 var propIdAreaId = new PropIdAreaId(); 833 propIdAreaId.propId = vhalPropIdAreaId.propId; 834 propIdAreaId.areaId = vhalPropIdAreaId.areaId; 835 propIdAreaIds.add(propIdAreaId); 836 } 837 return propIdAreaIds; 838 } 839 } 840 onGetValues(GetValueResults responses)841 private void onGetValues(GetValueResults responses) { 842 Trace.traceBegin(TRACE_TAG, "AidlVehicleStub#onGetValues"); 843 GetValueResults origResponses = (GetValueResults) 844 LargeParcelable.reconstructStableAIDLParcelable( 845 responses, responses.sharedMemoryFd, GetValueResults.CREATOR); 846 onGetSetValues(origResponses.payloads, new AsyncGetResultsHandler(mPropValueBuilder), 847 mPendingSyncGetValueRequestPool); 848 Trace.traceEnd(TRACE_TAG); 849 } 850 onSetValues(SetValueResults responses)851 private void onSetValues(SetValueResults responses) { 852 Trace.traceBegin(TRACE_TAG, "AidlVehicleStub#onSetValues"); 853 SetValueResults origResponses = (SetValueResults) 854 LargeParcelable.reconstructStableAIDLParcelable( 855 responses, responses.sharedMemoryFd, SetValueResults.CREATOR); 856 onGetSetValues(origResponses.payloads, new AsyncSetResultsHandler(), 857 mPendingSyncSetValueRequestPool); 858 Trace.traceEnd(TRACE_TAG); 859 } 860 861 /** 862 * A generic function for {@link onGetValues} / {@link onSetValues}. 863 */ onGetSetValues(VhalResultType[] vhalResults, AsyncResultsHandler asyncResultsHandler, PendingSyncRequestPool<VhalResultType> pendingSyncRequestPool)864 private <VhalResultType> void onGetSetValues(VhalResultType[] vhalResults, 865 AsyncResultsHandler asyncResultsHandler, 866 PendingSyncRequestPool<VhalResultType> pendingSyncRequestPool) { 867 synchronized (mLock) { 868 for (VhalResultType result : vhalResults) { 869 long vhalRequestId = asyncResultsHandler.getVhalRequestId(result); 870 if (!mPendingAsyncRequestPool.contains(vhalRequestId)) { 871 // If we cannot find the request Id in the async map, we assume it is for a 872 // sync request. 873 completePendingSyncRequestLocked(pendingSyncRequestPool, vhalRequestId, result); 874 continue; 875 } 876 877 AsyncRequestInfo requestInfo = mPendingAsyncRequestPool.finishRequestIfFound( 878 vhalRequestId, /* alreadyTimedOut= */ false); 879 if (requestInfo == null) { 880 Slogf.w(TAG, 881 "No pending request for ID: %s, possibly already timed out, " 882 + "or cancelled, or the client already died", vhalRequestId); 883 continue; 884 } 885 asyncResultsHandler.addVhalResult(requestInfo.getClientCallback(), 886 requestInfo.getServiceRequestId(), result); 887 } 888 } 889 Trace.traceBegin(TRACE_TAG, "AidlVehicleStub call async result callback"); 890 asyncResultsHandler.callVehicleStubCallback(); 891 Trace.traceEnd(TRACE_TAG); 892 } 893 printPropIdAreaId(HalPropValue value)894 private static String printPropIdAreaId(HalPropValue value) { 895 return "propID: " + value.getPropId() + ", areaID: " + value.getAreaId(); 896 } 897 898 private final class GetSetValuesCallback extends IVehicleCallback.Stub { 899 900 @Override onGetValues(GetValueResults responses)901 public void onGetValues(GetValueResults responses) throws RemoteException { 902 AidlVehicleStub.this.onGetValues(responses); 903 } 904 905 @Override onSetValues(SetValueResults responses)906 public void onSetValues(SetValueResults responses) throws RemoteException { 907 AidlVehicleStub.this.onSetValues(responses); 908 } 909 910 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) 911 @Override onPropertyEvent(VehiclePropValues propValues, int sharedMemoryFileCount)912 public void onPropertyEvent(VehiclePropValues propValues, int sharedMemoryFileCount) 913 throws RemoteException { 914 throwUnsupportedException(); 915 } 916 917 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) 918 @Override onPropertySetError(VehiclePropErrors errors)919 public void onPropertySetError(VehiclePropErrors errors) throws RemoteException { 920 throwUnsupportedException(); 921 } 922 923 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) 924 @Override onSupportedValueChange( List<android.hardware.automotive.vehicle.PropIdAreaId> propIdAreaIds)925 public void onSupportedValueChange( 926 List<android.hardware.automotive.vehicle.PropIdAreaId> propIdAreaIds) 927 throws RemoteException { 928 throwUnsupportedException(); 929 } 930 931 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) 932 @Override getInterfaceHash()933 public String getInterfaceHash() { 934 return IVehicleCallback.HASH; 935 } 936 937 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) 938 @Override getInterfaceVersion()939 public int getInterfaceVersion() { 940 return IVehicleCallback.VERSION; 941 } 942 943 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) throwUnsupportedException()944 private void throwUnsupportedException() { 945 throw new UnsupportedOperationException( 946 "GetSetValuesCallback only support onGetValues or onSetValues"); 947 } 948 } 949 950 /** 951 * Mark a pending sync get/set property request as complete and deliver the result. 952 */ 953 @GuardedBy("mLock") completePendingSyncRequestLocked( PendingSyncRequestPool<VhalResultType> pendingSyncRequestPool, long vhalRequestId, VhalResultType result)954 private <VhalResultType> void completePendingSyncRequestLocked( 955 PendingSyncRequestPool<VhalResultType> pendingSyncRequestPool, long vhalRequestId, 956 VhalResultType result) { 957 Trace.traceBegin(TRACE_TAG, "AidlVehicleStub#completePendingSyncRequestLocked"); 958 AndroidFuture<VhalResultType> pendingRequest = 959 pendingSyncRequestPool.finishRequestIfFound(vhalRequestId); 960 if (pendingRequest == null) { 961 Slogf.w(TAG, "No pending request for ID: " + vhalRequestId 962 + ", possibly already timed out"); 963 return; 964 } 965 966 Trace.traceBegin(TRACE_TAG, "AidlVehicleStub#complete pending request"); 967 // This might fail if the request already timed out. 968 pendingRequest.complete(result); 969 Trace.traceEnd(TRACE_TAG); 970 Trace.traceEnd(TRACE_TAG); 971 } 972 973 private static final class AsyncGetRequestsHandler 974 extends AsyncRequestsHandler<GetValueRequest, GetValueRequests> { 975 private GetValueRequest[] mVhalRequestItems; 976 private int mIndex; 977 978 @Override allocateVhalRequestSize(int size)979 public void allocateVhalRequestSize(int size) { 980 mVhalRequestItems = new GetValueRequest[size]; 981 } 982 983 @Override addVhalRequest(long vhalRequestId, HalPropValue halPropValue)984 public void addVhalRequest(long vhalRequestId, HalPropValue halPropValue) { 985 mVhalRequestItems[mIndex] = new GetValueRequest(); 986 mVhalRequestItems[mIndex].requestId = vhalRequestId; 987 mVhalRequestItems[mIndex].prop = (VehiclePropValue) halPropValue.toVehiclePropValue(); 988 mIndex++; 989 } 990 991 @Override getRequestItems()992 public GetValueRequest[] getRequestItems() { 993 return mVhalRequestItems; 994 } 995 996 @Override sendRequestsToVhal(IVehicle iVehicle, GetSetValuesCallback callbackForVhal)997 public void sendRequestsToVhal(IVehicle iVehicle, GetSetValuesCallback callbackForVhal) 998 throws RemoteException, ServiceSpecificException { 999 Trace.traceBegin(TRACE_TAG, "Prepare LargeParcelable"); 1000 GetValueRequests largeParcelableRequest = new GetValueRequests(); 1001 largeParcelableRequest.payloads = mVhalRequestItems; 1002 // TODO(b/269669729): Don't try to use large parcelable if the request size is too 1003 // small. 1004 largeParcelableRequest = (GetValueRequests) LargeParcelable.toLargeParcelable( 1005 largeParcelableRequest, () -> { 1006 GetValueRequests newRequests = new GetValueRequests(); 1007 newRequests.payloads = new GetValueRequest[0]; 1008 return newRequests; 1009 }); 1010 Trace.traceEnd(TRACE_TAG); 1011 1012 try { 1013 Trace.traceBegin(TRACE_TAG, "IVehicle#getValues"); 1014 iVehicle.getValues(callbackForVhal, largeParcelableRequest); 1015 } finally { 1016 LargeParcelable.closeFd(largeParcelableRequest.sharedMemoryFd); 1017 Trace.traceEnd(TRACE_TAG); 1018 } 1019 } 1020 1021 @Override getVhalRequestId(GetValueRequest request)1022 public long getVhalRequestId(GetValueRequest request) { 1023 return request.requestId; 1024 } 1025 } 1026 1027 private static final class AsyncSetRequestsHandler 1028 extends AsyncRequestsHandler<SetValueRequest, SetValueRequests> { 1029 private SetValueRequest[] mVhalRequestItems; 1030 private int mIndex; 1031 1032 @Override allocateVhalRequestSize(int size)1033 public void allocateVhalRequestSize(int size) { 1034 mVhalRequestItems = new SetValueRequest[size]; 1035 } 1036 1037 @Override addVhalRequest(long vhalRequestId, HalPropValue halPropValue)1038 public void addVhalRequest(long vhalRequestId, HalPropValue halPropValue) { 1039 mVhalRequestItems[mIndex] = new SetValueRequest(); 1040 mVhalRequestItems[mIndex].requestId = vhalRequestId; 1041 mVhalRequestItems[mIndex].value = (VehiclePropValue) halPropValue.toVehiclePropValue(); 1042 mIndex++; 1043 } 1044 1045 @Override getRequestItems()1046 public SetValueRequest[] getRequestItems() { 1047 return mVhalRequestItems; 1048 } 1049 1050 @Override sendRequestsToVhal(IVehicle iVehicle, GetSetValuesCallback callbackForVhal)1051 public void sendRequestsToVhal(IVehicle iVehicle, GetSetValuesCallback callbackForVhal) 1052 throws RemoteException, ServiceSpecificException { 1053 SetValueRequests largeParcelableRequest = new SetValueRequests(); 1054 largeParcelableRequest.payloads = mVhalRequestItems; 1055 largeParcelableRequest = (SetValueRequests) LargeParcelable.toLargeParcelable( 1056 largeParcelableRequest, () -> { 1057 SetValueRequests newRequests = new SetValueRequests(); 1058 newRequests.payloads = new SetValueRequest[0]; 1059 return newRequests; 1060 }); 1061 try { 1062 iVehicle.setValues(callbackForVhal, largeParcelableRequest); 1063 } finally { 1064 LargeParcelable.closeFd(largeParcelableRequest.sharedMemoryFd); 1065 } 1066 } 1067 1068 @Override getVhalRequestId(SetValueRequest request)1069 public long getVhalRequestId(SetValueRequest request) { 1070 return request.requestId; 1071 } 1072 } 1073 1074 private static final class AsyncGetResultsHandler extends 1075 AsyncResultsHandler<GetValueResult, GetVehicleStubAsyncResult> { 1076 private HalPropValueBuilder mPropValueBuilder; 1077 AsyncGetResultsHandler(HalPropValueBuilder propValueBuilder)1078 AsyncGetResultsHandler(HalPropValueBuilder propValueBuilder) { 1079 mPropValueBuilder = propValueBuilder; 1080 mCallbackToResults = new ArrayMap<VehicleStubCallbackInterface, 1081 List<GetVehicleStubAsyncResult>>(); 1082 } 1083 1084 @Override addErrorResult(VehicleStubCallbackInterface callback, int serviceRequestId, CarPropertyErrorCodes errorCodes)1085 void addErrorResult(VehicleStubCallbackInterface callback, int serviceRequestId, 1086 CarPropertyErrorCodes errorCodes) { 1087 addVehicleStubResult(callback, new GetVehicleStubAsyncResult(serviceRequestId, 1088 errorCodes)); 1089 } 1090 1091 @Override addVhalResult(VehicleStubCallbackInterface callback, int serviceRequestId, GetValueResult result)1092 void addVhalResult(VehicleStubCallbackInterface callback, int serviceRequestId, 1093 GetValueResult result) { 1094 addVehicleStubResult(callback, toVehicleStubResult(serviceRequestId, result)); 1095 } 1096 1097 @Override callVehicleStubCallback()1098 void callVehicleStubCallback() { 1099 for (Map.Entry<VehicleStubCallbackInterface, List<GetVehicleStubAsyncResult>> entry : 1100 mCallbackToResults.entrySet()) { 1101 entry.getKey().onGetAsyncResults(entry.getValue()); 1102 } 1103 } 1104 1105 @Override getVhalRequestId(GetValueResult result)1106 long getVhalRequestId(GetValueResult result) { 1107 return result.requestId; 1108 } 1109 toVehicleStubResult(int serviceRequestId, GetValueResult vhalResult)1110 private GetVehicleStubAsyncResult toVehicleStubResult(int serviceRequestId, 1111 GetValueResult vhalResult) { 1112 if (vhalResult.status != StatusCode.OK) { 1113 CarPropertyErrorCodes carPropertyErrorCodes = 1114 createFromVhalStatusCode(vhalResult.status); 1115 return new GetVehicleStubAsyncResult(serviceRequestId, carPropertyErrorCodes); 1116 } else if (vhalResult.prop == null) { 1117 // If status is OKAY but no property is returned, treat it as not_available. 1118 return new GetVehicleStubAsyncResult(serviceRequestId, 1119 CarPropertyErrorCodes.ERROR_CODES_NOT_AVAILABLE); 1120 } 1121 return new GetVehicleStubAsyncResult(serviceRequestId, 1122 mPropValueBuilder.build(vhalResult.prop)); 1123 } 1124 } 1125 1126 private static final class AsyncSetResultsHandler extends 1127 AsyncResultsHandler<SetValueResult, SetVehicleStubAsyncResult> { AsyncSetResultsHandler()1128 AsyncSetResultsHandler() { 1129 mCallbackToResults = new ArrayMap<VehicleStubCallbackInterface, 1130 List<SetVehicleStubAsyncResult>>(); 1131 } 1132 1133 @Override addErrorResult(VehicleStubCallbackInterface callback, int serviceRequestId, CarPropertyErrorCodes errorCodes)1134 void addErrorResult(VehicleStubCallbackInterface callback, int serviceRequestId, 1135 CarPropertyErrorCodes errorCodes) { 1136 addVehicleStubResult(callback, 1137 new SetVehicleStubAsyncResult(serviceRequestId, errorCodes)); 1138 } 1139 1140 @Override addVhalResult(VehicleStubCallbackInterface callback, int serviceRequestId, SetValueResult result)1141 void addVhalResult(VehicleStubCallbackInterface callback, int serviceRequestId, 1142 SetValueResult result) { 1143 addVehicleStubResult(callback, toVehicleStubResult(serviceRequestId, result)); 1144 1145 } 1146 1147 @Override callVehicleStubCallback()1148 void callVehicleStubCallback() { 1149 for (Map.Entry<VehicleStubCallbackInterface, List<SetVehicleStubAsyncResult>> entry : 1150 mCallbackToResults.entrySet()) { 1151 entry.getKey().onSetAsyncResults(entry.getValue()); 1152 } 1153 } 1154 1155 @Override getVhalRequestId(SetValueResult result)1156 long getVhalRequestId(SetValueResult result) { 1157 return result.requestId; 1158 } 1159 toVehicleStubResult(int serviceRequestId, SetValueResult vhalResult)1160 private SetVehicleStubAsyncResult toVehicleStubResult(int serviceRequestId, 1161 SetValueResult vhalResult) { 1162 if (vhalResult.status != StatusCode.OK) { 1163 CarPropertyErrorCodes carPropertyErrorCodes = 1164 createFromVhalStatusCode(vhalResult.status); 1165 return new SetVehicleStubAsyncResult(serviceRequestId, carPropertyErrorCodes); 1166 } 1167 return new SetVehicleStubAsyncResult(serviceRequestId); 1168 } 1169 } 1170 1171 /** 1172 * Generic function for {@link get} or {@link set}. 1173 */ getOrSetSync( HalPropValue requestedPropValue, PendingSyncRequestPool<VhalResultType> pendingSyncRequestPool, AsyncRequestsHandler requestsHandler, Function<VhalResultType, HalPropValue> resultHandler)1174 private <VhalResultType> HalPropValue getOrSetSync( 1175 HalPropValue requestedPropValue, 1176 PendingSyncRequestPool<VhalResultType> pendingSyncRequestPool, 1177 AsyncRequestsHandler requestsHandler, 1178 Function<VhalResultType, HalPropValue> resultHandler) 1179 throws RemoteException, ServiceSpecificException { 1180 Trace.traceBegin(TRACE_TAG, "AidlVehicleStub#getOrSetSync"); 1181 long vhalRequestId = mRequestId.getAndIncrement(); 1182 1183 AndroidFuture<VhalResultType> resultFuture = pendingSyncRequestPool.addRequest( 1184 vhalRequestId); 1185 1186 requestsHandler.allocateVhalRequestSize(1); 1187 requestsHandler.addVhalRequest(vhalRequestId, requestedPropValue); 1188 requestsHandler.sendRequestsToVhal(mAidlVehicle, mGetSetValuesCallback); 1189 1190 boolean gotResult = false; 1191 1192 try { 1193 Trace.traceBegin(TRACE_TAG, "AidlVehicleStub#waitingForSyncResult"); 1194 VhalResultType result = resultFuture.get(mSyncOpTimeoutInMs, 1195 TimeUnit.MILLISECONDS); 1196 gotResult = true; 1197 return resultHandler.apply(result); 1198 } catch (InterruptedException e) { 1199 Thread.currentThread().interrupt(); // Restore the interrupted status 1200 throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR, 1201 "thread interrupted, possibly exiting the thread"); 1202 } catch (ExecutionException e) { 1203 throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR, 1204 "failed to resolve future, error: " + e); 1205 } catch (TimeoutException e) { 1206 throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR, 1207 "get/set value request timeout for: " + printPropIdAreaId(requestedPropValue)); 1208 } finally { 1209 Trace.traceEnd(TRACE_TAG); 1210 if (!gotResult) { 1211 resultFuture = pendingSyncRequestPool.finishRequestIfFound(vhalRequestId); 1212 // Something wrong happened, the future is guaranteed not to be used again. 1213 resultFuture.cancel(/* mayInterruptIfRunning= */ false); 1214 } 1215 Trace.traceEnd(TRACE_TAG); 1216 } 1217 } 1218 1219 /** 1220 * Generic function for {@link getAsync} or {@link setAsync}. 1221 */ getOrSetAsync( List<AsyncGetSetRequest> vehicleStubAsyncRequests, VehicleStubCallbackInterface vehicleStubCallback, AsyncRequestsHandler<VhalRequestType, VhalRequestsType> asyncRequestsHandler, AsyncResultsHandler asyncResultsHandler)1222 private <VhalRequestType, VhalRequestsType> void getOrSetAsync( 1223 List<AsyncGetSetRequest> vehicleStubAsyncRequests, 1224 VehicleStubCallbackInterface vehicleStubCallback, 1225 AsyncRequestsHandler<VhalRequestType, VhalRequestsType> asyncRequestsHandler, 1226 AsyncResultsHandler asyncResultsHandler) { 1227 prepareAndConvertAsyncRequests(vehicleStubAsyncRequests, vehicleStubCallback, 1228 asyncRequestsHandler); 1229 1230 try { 1231 asyncRequestsHandler.sendRequestsToVhal(mAidlVehicle, mGetSetValuesCallback); 1232 } catch (RemoteException e) { 1233 handleAsyncExceptionFromVhal( 1234 asyncRequestsHandler, 1235 vehicleStubCallback, 1236 CarPropertyErrorCodes.ERROR_CODES_INTERNAL, 1237 asyncResultsHandler); 1238 return; 1239 } catch (ServiceSpecificException e) { 1240 CarPropertyErrorCodes carPropertyErrorCodes = 1241 createFromVhalStatusCode(e.errorCode); 1242 handleAsyncExceptionFromVhal(asyncRequestsHandler, vehicleStubCallback, 1243 carPropertyErrorCodes, asyncResultsHandler); 1244 return; 1245 } 1246 } 1247 1248 /** 1249 * Prepare an async get/set request from client and convert it to vhal requests. 1250 * 1251 * <p> It does the following things: 1252 * <ul> 1253 * <li> Add a client callback death listener which will clear the pending requests when client 1254 * died 1255 * <li> Store the async requests to a pending request map. 1256 * <li> For each client request, generate a unique VHAL request ID and convert the request to 1257 * VHAL request type. 1258 * <li> Stores the time-out information for each request into a map so that we can register 1259 * timeout handlers later. 1260 * <li> Convert the vhal request items to a single large parcelable class. 1261 */ prepareAndConvertAsyncRequests( List<AsyncGetSetRequest> vehicleStubRequests, VehicleStubCallbackInterface clientCallback, AsyncRequestsHandler<VhalRequestType, VhalRequestsType> asyncRequestsHandler)1262 private <VhalRequestType, VhalRequestsType> void prepareAndConvertAsyncRequests( 1263 List<AsyncGetSetRequest> vehicleStubRequests, 1264 VehicleStubCallbackInterface clientCallback, 1265 AsyncRequestsHandler<VhalRequestType, VhalRequestsType> asyncRequestsHandler) { 1266 asyncRequestsHandler.allocateVhalRequestSize(vehicleStubRequests.size()); 1267 synchronized (mLock) { 1268 // Add the death recipient so that all client info for a dead callback will be cleaned 1269 // up. Note that this must be in the same critical section as the following code to 1270 // store the client info into the map. This makes sure that even if the client is 1271 // died half way while adding the client info, it will wait until all the clients are 1272 // added and then remove them all. 1273 try { 1274 clientCallback.linkToDeath(() -> { 1275 // This function will be invoked from a different thread. It needs to be 1276 // guarded by a lock so that the whole 'prepareAndConvertAsyncRequests' finishes 1277 // before we remove the callback. 1278 synchronized (mLock) { 1279 mPendingAsyncRequestPool.removeRequestsForCallback(clientCallback); 1280 } 1281 }); 1282 } catch (RemoteException e) { 1283 // The binder is already died. 1284 throw new IllegalStateException("Failed to link callback to death recipient, the " 1285 + "client maybe already died"); 1286 } 1287 1288 List<AsyncRequestInfo> requestInfoList = new ArrayList<>(); 1289 for (int i = 0; i < vehicleStubRequests.size(); i++) { 1290 AsyncGetSetRequest vehicleStubRequest = vehicleStubRequests.get(i); 1291 long vhalRequestId = mRequestId.getAndIncrement(); 1292 asyncRequestsHandler.addVhalRequest(vhalRequestId, 1293 vehicleStubRequest.getHalPropValue()); 1294 requestInfoList.add(new AsyncRequestInfo( 1295 vhalRequestId, vehicleStubRequest.getServiceRequestId(), clientCallback, 1296 vehicleStubRequest.getTimeoutUptimeMs())); 1297 } 1298 mPendingAsyncRequestPool.addRequests(requestInfoList); 1299 } 1300 1301 } 1302 1303 /** 1304 * Callback to deliver async get/set error results back to the client. 1305 * 1306 * <p>When an exception is received, the callback delivers the error results on the same thread 1307 * where the caller is. 1308 */ handleAsyncExceptionFromVhal( AsyncRequestsHandler<VhalRequestType, VhalRequestsType> asyncRequestsHandler, VehicleStubCallbackInterface vehicleStubCallback, CarPropertyErrorCodes errorCodes, AsyncResultsHandler asyncResultsHandler)1309 private <VhalRequestType, VhalRequestsType> void handleAsyncExceptionFromVhal( 1310 AsyncRequestsHandler<VhalRequestType, VhalRequestsType> asyncRequestsHandler, 1311 VehicleStubCallbackInterface vehicleStubCallback, CarPropertyErrorCodes errorCodes, 1312 AsyncResultsHandler asyncResultsHandler) { 1313 Slogf.w(TAG, 1314 "Received RemoteException or ServiceSpecificException from VHAL. VHAL is likely " 1315 + "dead, error code: " + errorCodes); 1316 synchronized (mLock) { 1317 VhalRequestType[] requests = asyncRequestsHandler.getRequestItems(); 1318 for (int i = 0; i < requests.length; i++) { 1319 long vhalRequestId = asyncRequestsHandler.getVhalRequestId(requests[i]); 1320 AsyncRequestInfo requestInfo = mPendingAsyncRequestPool.finishRequestIfFound( 1321 vhalRequestId, /* alreadyTimedOut= */ false); 1322 if (requestInfo == null) { 1323 Slogf.w(TAG, 1324 "No pending request for ID: %s, possibly already timed out or " 1325 + "the client already died", vhalRequestId); 1326 continue; 1327 } 1328 asyncResultsHandler.addErrorResult( 1329 vehicleStubCallback, requestInfo.getServiceRequestId(), errorCodes); 1330 } 1331 } 1332 asyncResultsHandler.callVehicleStubCallback(); 1333 } 1334 1335 } 1336