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