• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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