1 /*
2  * Copyright (C) 2018 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 package com.android.car.hal;
17 
18 import static android.car.hardware.CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_STATIC;
19 import static android.car.hardware.property.CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_ACCESS_DENIED;
20 import static android.car.hardware.property.CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_INVALID_ARG;
21 import static android.car.hardware.property.CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_PROPERTY_NOT_AVAILABLE;
22 import static android.car.hardware.property.CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_TRY_AGAIN;
23 import static android.car.hardware.property.CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN;
24 import static android.car.hardware.property.VehicleHalStatusCode.STATUS_ACCESS_DENIED;
25 import static android.car.hardware.property.VehicleHalStatusCode.STATUS_INTERNAL_ERROR;
26 import static android.car.hardware.property.VehicleHalStatusCode.STATUS_INVALID_ARG;
27 import static android.car.hardware.property.VehicleHalStatusCode.STATUS_NOT_AVAILABLE;
28 import static android.car.hardware.property.VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED;
29 import static android.car.hardware.property.VehicleHalStatusCode.STATUS_NOT_AVAILABLE_POOR_VISIBILITY;
30 import static android.car.hardware.property.VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY;
31 import static android.car.hardware.property.VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH;
32 import static android.car.hardware.property.VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW;
33 import static android.car.hardware.property.VehicleHalStatusCode.STATUS_TRY_AGAIN;
34 
35 import static com.android.car.internal.common.CommonConstants.EMPTY_INT_ARRAY;
36 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
37 import static com.android.car.internal.property.CarPropertyErrorCodes.convertVhalStatusCodeToCarPropertyManagerErrorCodes;
38 import static com.android.car.internal.property.CarPropertyErrorCodes.STATUS_OK;
39 import static com.android.car.internal.property.CarPropertyHelper.isSystemProperty;
40 import static com.android.car.internal.property.GetSetValueResult.newGetValueResult;
41 import static com.android.car.internal.property.InputSanitizationUtils.sanitizeUpdateRateHz;
42 
43 import android.annotation.IntDef;
44 import android.annotation.Nullable;
45 import android.car.VehiclePropertyIds;
46 import android.car.builtin.os.BuildHelper;
47 import android.car.builtin.util.Slogf;
48 import android.car.hardware.CarPropertyConfig;
49 import android.car.hardware.CarPropertyValue;
50 import android.car.hardware.property.CarPropertyEvent;
51 import android.car.hardware.property.CarPropertyManager;
52 import android.car.hardware.property.CarPropertyManager.CarSetPropertyErrorCode;
53 import android.car.hardware.property.VehicleHalStatusCode.VehicleHalStatusCodeInt;
54 import android.content.Context;
55 import android.hardware.automotive.vehicle.VehiclePropError;
56 import android.hardware.automotive.vehicle.VehicleProperty;
57 import android.hardware.automotive.vehicle.VehiclePropertyStatus;
58 import android.os.Handler;
59 import android.os.HandlerThread;
60 import android.os.IBinder;
61 import android.os.IBinder.DeathRecipient;
62 import android.os.RemoteException;
63 import android.os.ServiceSpecificException;
64 import android.os.SystemClock;
65 import android.util.ArrayMap;
66 import android.util.ArraySet;
67 import android.util.Log;
68 import android.util.SparseArray;
69 
70 import com.android.car.CarLog;
71 import com.android.car.CarServiceUtils;
72 import com.android.car.VehicleStub;
73 import com.android.car.VehicleStub.AsyncGetSetRequest;
74 import com.android.car.VehicleStub.GetVehicleStubAsyncResult;
75 import com.android.car.VehicleStub.SetVehicleStubAsyncResult;
76 import com.android.car.VehicleStub.VehicleStubCallbackInterface;
77 import com.android.car.hal.VehicleHal.HalSubscribeOptions;
78 import com.android.car.hal.property.PropertyHalServiceConfigs;
79 import com.android.car.hal.property.PropertyPermissionInfo.PermissionCondition;
80 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
81 import com.android.car.internal.LongPendingRequestPool;
82 import com.android.car.internal.LongPendingRequestPool.TimeoutCallback;
83 import com.android.car.internal.LongRequestIdWithTimeout;
84 import com.android.car.internal.property.AsyncPropertyServiceRequest;
85 import com.android.car.internal.property.CarPropertyErrorCodes;
86 import com.android.car.internal.property.CarPropertyHelper;
87 import com.android.car.internal.property.CarSubscription;
88 import com.android.car.internal.property.GetSetValueResult;
89 import com.android.car.internal.property.GetSetValueResultList;
90 import com.android.car.internal.property.IAsyncPropertyResultCallback;
91 import com.android.car.internal.property.SubscriptionManager;
92 import com.android.car.internal.util.IndentingPrintWriter;
93 import com.android.car.internal.util.PairSparseArray;
94 import com.android.internal.annotations.GuardedBy;
95 import com.android.internal.annotations.VisibleForTesting;
96 import com.android.modules.expresslog.Histogram;
97 
98 import java.io.PrintWriter;
99 import java.lang.annotation.Retention;
100 import java.lang.annotation.RetentionPolicy;
101 import java.util.ArrayList;
102 import java.util.Collection;
103 import java.util.List;
104 import java.util.Map;
105 import java.util.Set;
106 import java.util.concurrent.atomic.AtomicInteger;
107 import java.util.function.Function;
108 
109 /**
110  * Common interface for HAL services that send Vehicle Properties back and forth via ICarProperty.
111  * Services that communicate by passing vehicle properties back and forth via ICarProperty should
112  * extend this class.
113  */
114 public class PropertyHalService extends HalServiceBase {
115     private static final String TAG = CarLog.tagFor(PropertyHalService.class);
116     private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG);
117     private static final int ASYNC_RETRY_SLEEP_IN_MS = 100;
118 
119     // Async get request from user.
120     private static final int GET = 0;
121     // Async set request from user.
122     private static final int SET = 1;
123     // Async get request for getting initial value when user issues async set property request.
124     // The reason we need to get initial value is that if the value to be set is the same as
125     // the current value, there might not be a property update event generated. In this case,
126     // it should be considered a success. If we get the initial value successfully and the
127     // initial value is the same as the target value, we treat the async set as success.
128     private static final int GET_INITIAL_VALUE_FOR_SET = 2;
129 
130     // A fake pending request ID for car property service.
131     private static final int CAR_PROP_SVC_REQUEST_ID = -1;
132 
133     // Only changed in testing.
134     private PropertyHalServiceConfigs mPropertyHalServiceConfigs =
135             PropertyHalServiceConfigs.getInstance();
136 
137     @GuardedBy("mLock")
138     private final PairSparseArray<CarPropertyValue> mStaticPropertyIdAreaIdCache =
139             new PairSparseArray<>();
140 
141     private final Histogram mGetAsyncEndToEndLatencyHistogram = new Histogram(
142             "automotive_os.value_get_async_end_to_end_latency",
143             new Histogram.ScaledRangeOptions(/* binCount= */ 20, /* minValue= */ 0,
144                     /* firstBinWidth= */ 2, /* scaleFactor= */ 1.5f));
145 
146     private final Histogram mSetAsyncEndToEndLatencyHistogram = new Histogram(
147             "automotive_os.value_set_async_end_to_end_latency",
148             new Histogram.ScaledRangeOptions(/* binCount= */ 20, /* minValue= */ 0,
149                     /* firstBinWidth= */ 2, /* scaleFactor= */ 1.5f));
150 
151     // Different type of async get/set property requests.
152     @IntDef({GET, SET, GET_INITIAL_VALUE_FOR_SET})
153     @Retention(RetentionPolicy.SOURCE)
154     private @interface AsyncRequestType {}
155 
ClientType(Integer requestId)156     public record ClientType(Integer requestId) {
157         @Override
158         public String toString() {
159             if (requestId == CAR_PROP_SVC_REQUEST_ID) {
160                 return "PropertyHalService.subscribeProperty";
161             }
162             return "PropertyHalService.setCarPropertyValuesAsync(requestId="
163                     + requestId.toString() + ")";
164         }
165     }
166 
167     private static final class GetSetValueResultWrapper {
168         private GetSetValueResult mGetSetValueResult;
169         private long mAsyncRequestStartTime;
170         private final int mRetryCount;
171 
GetSetValueResultWrapper(GetSetValueResult getSetValueResult, long asyncRequestStartTime, int retryCount)172         private GetSetValueResultWrapper(GetSetValueResult getSetValueResult,
173                 long asyncRequestStartTime, int retryCount) {
174             mGetSetValueResult = getSetValueResult;
175             mAsyncRequestStartTime = asyncRequestStartTime;
176             mRetryCount = retryCount;
177         }
178 
getGetSetValueResult()179         private GetSetValueResult getGetSetValueResult() {
180             return mGetSetValueResult;
181         }
182 
getAsyncRequestStartTime()183         private long getAsyncRequestStartTime() {
184             return mAsyncRequestStartTime;
185         }
186 
getRetryCount()187         private int getRetryCount() {
188             return mRetryCount;
189         }
190     }
191 
192     private static final class AsyncPropRequestInfo implements LongRequestIdWithTimeout {
193         private final AsyncPropertyServiceRequest mPropMgrRequest;
194         // The uptimeMillis when this request time out.
195         private final long mTimeoutUptimeMs;
196         private final @AsyncRequestType int mRequestType;
197         private final VehicleStubCallback mVehicleStubCallback;
198         private final long mAsyncRequestStartTime;
199         private boolean mSetRequestSent;
200         private long mUpdateTimestampNanos;
201         private boolean mValueUpdated;
202         private int mServiceRequestId;
203         private float mUpdateRateHz;
204         private int mRetryCount;
205         // The associated async set request for get_initial_value request.
206         private @Nullable AsyncPropRequestInfo mAssocSetValueRequestInfo;
207         // The associated get initial value request for async set request.
208         private @Nullable AsyncPropRequestInfo mAssocGetInitValueRequestInfo;
209 
AsyncPropRequestInfo(@syncRequestType int requestType, AsyncPropertyServiceRequest propMgrRequest, long timeoutUptimeMs, VehicleStubCallback vehicleStubCallback, long asyncRequestStartTime)210         AsyncPropRequestInfo(@AsyncRequestType int requestType,
211                 AsyncPropertyServiceRequest propMgrRequest,
212                 long timeoutUptimeMs, VehicleStubCallback vehicleStubCallback,
213                 long asyncRequestStartTime) {
214             mPropMgrRequest = propMgrRequest;
215             mTimeoutUptimeMs = timeoutUptimeMs;
216             mRequestType = requestType;
217             mVehicleStubCallback = vehicleStubCallback;
218             mAsyncRequestStartTime = asyncRequestStartTime;
219         }
220 
getRequestType()221         private @AsyncRequestType int getRequestType() {
222             return mRequestType;
223         }
224 
getManagerRequestId()225         private int getManagerRequestId() {
226             return mPropMgrRequest.getRequestId();
227         }
228 
229 
getPropertyName()230         private String getPropertyName() {
231             return VehiclePropertyIds.toString(getPropertyId());
232         }
233 
getPropertyId()234         int getPropertyId() {
235             return mPropMgrRequest.getPropertyId();
236         }
237 
getAreaId()238         int getAreaId() {
239             return mPropMgrRequest.getAreaId();
240         }
241 
getUpdateTimestampNanos()242         public long getUpdateTimestampNanos() {
243             return mUpdateTimestampNanos;
244         }
245 
getPropSvcRequest()246         AsyncPropertyServiceRequest getPropSvcRequest() {
247             return mPropMgrRequest;
248         }
249 
toErrorResult(CarPropertyErrorCodes errorCodes)250         GetSetValueResult toErrorResult(CarPropertyErrorCodes errorCodes) {
251             return GetSetValueResult.newErrorResult(getManagerRequestId(), errorCodes);
252         }
253 
toGetValueResult(CarPropertyValue value)254         GetSetValueResult toGetValueResult(CarPropertyValue value) {
255             return newGetValueResult(getManagerRequestId(), value);
256         }
257 
toSetValueResult(long updateTimestampNanos)258         GetSetValueResult toSetValueResult(long updateTimestampNanos) {
259             return GetSetValueResult.newSetValueResult(getManagerRequestId(),
260                     updateTimestampNanos);
261         }
262 
setSetRequestSent()263         void setSetRequestSent() {
264             mSetRequestSent = true;
265         }
266 
setValueUpdated(long updateTimestampNanos)267         void setValueUpdated(long updateTimestampNanos) {
268             mValueUpdated = true;
269             mUpdateTimestampNanos = updateTimestampNanos;
270         }
271 
isWaitForPropertyUpdate()272         boolean isWaitForPropertyUpdate() {
273             return mPropMgrRequest.isWaitForPropertyUpdate();
274         }
275 
success()276         boolean success() {
277             // If the set request is sent and either we don't wait for property update or the
278             // property update happened (which includes the initial value is already the target
279             // value)
280             return mSetRequestSent && (!isWaitForPropertyUpdate() || mValueUpdated);
281         }
282 
setAssocSetValueRequestInfo(AsyncPropRequestInfo requestInfo)283         void setAssocSetValueRequestInfo(AsyncPropRequestInfo requestInfo) {
284             mAssocSetValueRequestInfo = requestInfo;
285         }
286 
getAssocSetValueRequestInfo()287         @Nullable AsyncPropRequestInfo getAssocSetValueRequestInfo() {
288             return mAssocSetValueRequestInfo;
289         }
290 
setAssocGetInitValueRequestInfo(AsyncPropRequestInfo requestInfo)291         void setAssocGetInitValueRequestInfo(AsyncPropRequestInfo requestInfo) {
292             mAssocGetInitValueRequestInfo = requestInfo;
293         }
294 
getAssocGetInitValueRequestInfo()295         @Nullable AsyncPropRequestInfo getAssocGetInitValueRequestInfo() {
296             return mAssocGetInitValueRequestInfo;
297         }
298 
setServiceRequestId(int serviceRequestId)299         void setServiceRequestId(int serviceRequestId) {
300             mServiceRequestId = serviceRequestId;
301         }
302 
getServiceRequestId()303         int getServiceRequestId() {
304             return mServiceRequestId;
305         }
306 
getVehicleStubCallback()307         VehicleStubCallback getVehicleStubCallback() {
308             return mVehicleStubCallback;
309         }
310 
getUpdateRateHz()311         float getUpdateRateHz() {
312             return mUpdateRateHz;
313         }
314 
incrementRetryCount()315         void incrementRetryCount() {
316             mRetryCount++;
317         }
318 
getRetryCount()319         int getRetryCount() {
320             return mRetryCount;
321         }
322 
323         /**
324          * Parses the updateRateHz from client and sanitize it.
325          */
parseClientUpdateRateHz(CarPropertyConfig carPropertyConfig)326         void parseClientUpdateRateHz(CarPropertyConfig carPropertyConfig) {
327             float clientUpdateRateHz = mPropMgrRequest.getUpdateRateHz();
328             if (clientUpdateRateHz == 0.0f) {
329                 // If client does not specify a update rate for async set, subscribe at the max
330                 // update rate so that we can get the property update as soon as possible.
331                 clientUpdateRateHz = carPropertyConfig.getMaxSampleRate();
332             }
333             mUpdateRateHz = sanitizeUpdateRateHz(carPropertyConfig, clientUpdateRateHz);
334         }
335 
336         @Override
getTimeoutUptimeMs()337         public long getTimeoutUptimeMs() {
338             return mTimeoutUptimeMs;
339         }
340 
341         @Override
getRequestId()342         public long getRequestId() {
343             return getServiceRequestId();
344         }
345 
getAsyncRequestStartTime()346         public long getAsyncRequestStartTime() {
347             return mAsyncRequestStartTime;
348         }
349 
350         @Override
351         @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
toString()352         public String toString() {
353             return new StringBuilder()
354                     .append("AsyncPropRequestInfo{type: ")
355                     .append(requestTypeToString(mRequestType))
356                     .append(", mgrRequestId: ")
357                     .append(getManagerRequestId())
358                     .append(", property: ")
359                     .append(getPropertyName())
360                     .append(", areaId: ")
361                     .append(getAreaId())
362                     .append(", timeout at uptime: ")
363                     .append(getTimeoutUptimeMs()).append("ms")
364                     .append(", serviceRequestId: ")
365                     .append(getServiceRequestId())
366                     .append(", update rate: ")
367                     .append(getUpdateRateHz()).append("hz")
368                     .append(", value updated for set: ")
369                     .append(mValueUpdated)
370                     .append(", request sent for set: ")
371                     .append(mSetRequestSent)
372                     .append("}").toString();
373         }
374     };
375 
376     // The request ID passed by CarPropertyService (ManagerRequestId) is directly passed from
377     // CarPropertyManager. Multiple CarPropertyManagers use the same car service instance, thus,
378     // the ManagerRequestId is not unique. We have to create another unique ID called
379     // ServiceRequestId and pass it to underlying layer (VehicleHal and VehicleStub).
380     // Internally, we will map ManagerRequestId to ServiceRequestId.
381     private final AtomicInteger mServiceRequestIdCounter = new AtomicInteger(0);
382     private final VehicleHal mVehicleHal;
383     private final HalPropValueBuilder mPropValueBuilder;
384     private final HandlerThread mHandlerThread =
385             CarServiceUtils.getHandlerThread(getClass().getSimpleName());
386     private final Handler mHandler = new Handler(mHandlerThread.getLooper());
387     private final TimeoutCallback mTimeoutCallback = new AsyncRequestTimeoutCallback();
388 
389     private final Object mLock = new Object();
390     @GuardedBy("mLock")
391     private final Map<IBinder, VehicleStubCallback>
392             mResultBinderToVehicleStubCallback = new ArrayMap<>();
393     @GuardedBy("mLock")
394     private final SparseArray<HalPropConfig> mHalPropIdToPropConfig =
395             new SparseArray<>();
396     // A pending request pool to store all pending async get/set property request info.
397     // Service request ID is int, not long, but we only have one version of PendingRequestPool.
398     @GuardedBy("mLock")
399     private final LongPendingRequestPool<AsyncPropRequestInfo> mPendingAsyncRequests =
400             new LongPendingRequestPool<>(mHandler.getLooper(), mTimeoutCallback);
401     @GuardedBy("mLock")
402     private PropertyHalListener mPropertyHalListener;
403     // A map to store pending async set request info that are currently waiting for property update
404     // events.
405     @GuardedBy("mLock")
406     private final SparseArray<List<AsyncPropRequestInfo>> mHalPropIdToWaitingUpdateRequestInfo =
407             new SparseArray<>();
408 
409     // CarPropertyService subscribes to properties through PropertyHalService. Meanwhile,
410     // PropertyHalService internally also subscribes to some property for async set operations.
411     // We need to merge both of these subscription rate to VHAL.
412     //
413     // This manager uses async property request ID as key to store subscription caused by async
414     // set operations. It uses CAR_PROP_SVC_REQUEST_ID as a fake key to store subscription caused
415     // by car property service.
416     //
417     // For example, if we internally subscribed to [propA, areaA] at 10hz, client requests at 20hz,
418     // then we need to tell VHAL to update the rate to 20hz. If we internally subscribed at 20hz,
419     // client requests at 10hz, then we should do nothing, however, if we internally unsubscribe,
420     // then the [propA, areaA] should be subscribed at 10hz.
421     @GuardedBy("mLock")
422     private final SubscriptionManager<ClientType> mSubManager = new SubscriptionManager<>();
423 
424     private class AsyncRequestTimeoutCallback implements TimeoutCallback {
425         @Override
onRequestsTimeout(List<Long> serviceRequestIds)426         public void onRequestsTimeout(List<Long> serviceRequestIds) {
427             ArrayMap<VehicleStubCallback, List<Integer>> callbackToRequestIds = new ArrayMap<>();
428             synchronized (mLock) {
429                 // Get the callback for the pending requests.
430                 for (int i = 0; i < serviceRequestIds.size(); i++) {
431                     // Service ID is always a valid int.
432                     int serviceRequestId = serviceRequestIds.get(i).intValue();
433                     AsyncPropRequestInfo requestInfo =
434                             getPendingAsyncPropRequestInfoLocked(serviceRequestId);
435                     if (requestInfo == null) {
436                         Slogf.w(TAG, "The pending request: %d finished before timeout handler",
437                                 serviceRequestId);
438                         continue;
439                     }
440                     VehicleStubCallback callback = requestInfo.getVehicleStubCallback();
441                     if (callbackToRequestIds.get(callback) == null) {
442                         callbackToRequestIds.put(callback, new ArrayList<>());
443                     }
444                     callbackToRequestIds.get(callback).add(serviceRequestId);
445                 }
446             }
447             for (int i = 0; i < callbackToRequestIds.size(); i++) {
448                 callbackToRequestIds.keyAt(i).onRequestsTimeout(callbackToRequestIds.valueAt(i));
449             }
450         }
451     }
452 
453     private class VehicleStubCallback extends VehicleStubCallbackInterface {
454         private final IAsyncPropertyResultCallback mAsyncPropertyResultCallback;
455         private final IBinder mClientBinder;
456 
VehicleStubCallback( IAsyncPropertyResultCallback asyncPropertyResultCallback)457         VehicleStubCallback(
458                 IAsyncPropertyResultCallback asyncPropertyResultCallback) {
459             mAsyncPropertyResultCallback = asyncPropertyResultCallback;
460             mClientBinder = asyncPropertyResultCallback.asBinder();
461         }
462 
logAndReturnResults(Histogram histogram, List<GetSetValueResultWrapper> getSetValueResultWrapperList, @AsyncRequestType int asyncRequestType)463         private static List<GetSetValueResult> logAndReturnResults(Histogram histogram,
464                 List<GetSetValueResultWrapper> getSetValueResultWrapperList,
465                 @AsyncRequestType int asyncRequestType) {
466             List<GetSetValueResult> getSetValueResults = new ArrayList<>();
467             float systemCurrentTimeMillis = (float) System.currentTimeMillis();
468             for (int i = 0; i < getSetValueResultWrapperList.size(); i++) {
469                 GetSetValueResultWrapper getSetValueResultWrapper =
470                         getSetValueResultWrapperList.get(i);
471                 GetSetValueResult getSetValueResult = getSetValueResultWrapper
472                         .getGetSetValueResult();
473                 histogram.logSample(systemCurrentTimeMillis
474                                 - getSetValueResultWrapper.getAsyncRequestStartTime());
475                 getSetValueResults.add(getSetValueResult);
476                 if (DBG) {
477                     Slogf.d(TAG, "E2E latency for %sPropertiesAsync for requestId: %d is %d",
478                             requestTypeToString(asyncRequestType), getSetValueResult.getRequestId(),
479                             getSetValueResultWrapper.getAsyncRequestStartTime());
480                 }
481                 if (getSetValueResultWrapper.getRetryCount() != 0) {
482                     Slogf.i(TAG, "Async %s request finished after retry, requestID: %d,"
483                                     + " CarPropertyValue: %s , retry count: %d",
484                             requestTypeToString(asyncRequestType),
485                             getSetValueResult.getRequestId(),
486                             getSetValueResult.getCarPropertyValue(),
487                             getSetValueResultWrapper.getRetryCount());
488                 }
489             }
490             return getSetValueResults;
491         }
492 
sendGetValueResults(List<GetSetValueResultWrapper> results)493         private void sendGetValueResults(List<GetSetValueResultWrapper> results) {
494             if (results.isEmpty()) {
495                 return;
496             }
497             List<GetSetValueResult> getSetValueResults = logAndReturnResults(
498                     mGetAsyncEndToEndLatencyHistogram, results, GET);
499             try {
500                 mAsyncPropertyResultCallback.onGetValueResults(
501                         new GetSetValueResultList(getSetValueResults));
502             } catch (RemoteException e) {
503                 Slogf.w(TAG, "sendGetValueResults: Client might have died already", e);
504             }
505         }
506 
sendSetValueResults(List<GetSetValueResultWrapper> results)507         void sendSetValueResults(List<GetSetValueResultWrapper> results) {
508             if (results.isEmpty()) {
509                 return;
510             }
511             List<GetSetValueResult> getSetValueResults = logAndReturnResults(
512                     mSetAsyncEndToEndLatencyHistogram, results, SET);
513             try {
514                 mAsyncPropertyResultCallback.onSetValueResults(
515                         new GetSetValueResultList(getSetValueResults));
516             } catch (RemoteException e) {
517                 Slogf.w(TAG, "sendSetValueResults: Client might have died already", e);
518             }
519         }
520 
retryIfNotExpired(List<AsyncPropRequestInfo> retryRequests)521         private void retryIfNotExpired(List<AsyncPropRequestInfo> retryRequests) {
522             List<AsyncGetSetRequest> vehicleStubAsyncGetRequests = new ArrayList<>();
523             List<GetSetValueResultWrapper> timeoutGetResults = new ArrayList<>();
524             List<AsyncGetSetRequest> vehicleStubAsyncSetRequests = new ArrayList<>();
525             List<GetSetValueResultWrapper> timeoutSetResults = new ArrayList<>();
526             List<AsyncPropRequestInfo> pendingRetryRequests = new ArrayList<>();
527             synchronized (mLock) {
528                 // Get the current time after obtaining lock since it might take some time to get
529                 // the lock.
530                 long currentUptimeMs = SystemClock.uptimeMillis();
531                 for (int i = 0; i < retryRequests.size(); i++) {
532                     AsyncPropRequestInfo requestInfo = retryRequests.get(i);
533                     requestInfo.incrementRetryCount();
534                     long timeoutUptimeMs = requestInfo.getTimeoutUptimeMs();
535                     if (timeoutUptimeMs <= currentUptimeMs) {
536                         // The request already expired.
537                         generateTimeoutResult(requestInfo, timeoutGetResults, timeoutSetResults);
538                         continue;
539                     }
540 
541                     // Generate a new service request ID and async request object for the retry.
542                     AsyncGetSetRequest vehicleStubAsyncRequest =
543                             generateVehicleStubAsyncRequestLocked(requestInfo);
544                     pendingRetryRequests.add(requestInfo);
545 
546                     switch (requestInfo.getRequestType()) {
547                         case GET: // fallthrough
548                         case GET_INITIAL_VALUE_FOR_SET:
549                             vehicleStubAsyncGetRequests.add(vehicleStubAsyncRequest);
550                             break;
551                         case SET:
552                             vehicleStubAsyncSetRequests.add(vehicleStubAsyncRequest);
553                             break;
554                     }
555                 }
556 
557                 // We already marked all the input requests as finished. Now for the new retry
558                 // requests, we need to put them back into the pending request pool.
559                 mPendingAsyncRequests.addPendingRequests(pendingRetryRequests);
560             }
561 
562             sendGetValueResults(timeoutGetResults);
563             if (!vehicleStubAsyncGetRequests.isEmpty()) {
564                 mVehicleHal.getAsync(vehicleStubAsyncGetRequests, this);
565             }
566             sendSetValueResults(timeoutSetResults);
567             if (!vehicleStubAsyncSetRequests.isEmpty()) {
568                 mVehicleHal.setAsync(vehicleStubAsyncSetRequests, this);
569             }
570         }
571 
getClientBinder()572         IBinder getClientBinder() {
573             return mClientBinder;
574         }
575 
576         // This is a wrapper for death recipient that will unlink itself upon binder death.
577         private final class DeathRecipientWrapper implements DeathRecipient {
578             private DeathRecipient mInnerRecipient;
579 
DeathRecipientWrapper(DeathRecipient innerRecipient)580             DeathRecipientWrapper(DeathRecipient innerRecipient) {
581                 mInnerRecipient = innerRecipient;
582             }
583 
584             @Override
binderDied()585             public void binderDied() {
586                 mInnerRecipient.binderDied();
587                 mClientBinder.unlinkToDeath(this, /* flags= */ 0);
588             }
589         }
590 
591         @Override
linkToDeath(DeathRecipient recipient)592         public void linkToDeath(DeathRecipient recipient) throws RemoteException {
593             mClientBinder.linkToDeath(new DeathRecipientWrapper(recipient),
594                     /* flags= */ 0);
595         }
596 
597         // Parses an async getProperty result and convert it to an okay/error result.
parseGetAsyncResults( GetVehicleStubAsyncResult getVehicleStubAsyncResult, AsyncPropRequestInfo clientRequestInfo)598         private GetSetValueResult parseGetAsyncResults(
599                 GetVehicleStubAsyncResult getVehicleStubAsyncResult,
600                 AsyncPropRequestInfo clientRequestInfo) {
601             int carPropMgrErrorCode = getVehicleStubAsyncResult.getErrorCode();
602             if (carPropMgrErrorCode != STATUS_OK) {
603                 // All other error results will be delivered back through callback.
604                 return clientRequestInfo.toErrorResult(
605                         getVehicleStubAsyncResult.getCarPropertyErrorCodes());
606             }
607 
608             // For okay status, convert the property value to the type the client expects.
609             int mgrPropId = clientRequestInfo.getPropertyId();
610             int halPropId = managerToHalPropId(mgrPropId);
611             HalPropConfig halPropConfig;
612             synchronized (mLock) {
613                 halPropConfig = mHalPropIdToPropConfig.get(halPropId);
614             }
615             if (halPropConfig == null) {
616                 Slogf.e(TAG, "No configuration found for property: %s, must not happen",
617                         clientRequestInfo.getPropertyName());
618                 return clientRequestInfo.toErrorResult(
619                         new CarPropertyErrorCodes(
620                                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR,
621                                 /* vendorErrorCode= */ 0,
622                                 /* systemErrorCode= */ 0));
623             }
624             HalPropValue halPropValue = getVehicleStubAsyncResult.getHalPropValue();
625             if (halPropValue.getStatus() == VehiclePropertyStatus.UNAVAILABLE) {
626                 return clientRequestInfo.toErrorResult(
627                         new CarPropertyErrorCodes(
628                                 CarPropertyManager.STATUS_ERROR_NOT_AVAILABLE,
629                                 /* vendorErrorCode= */ 0,
630                                 /* systemErrorCode= */ 0));
631             }
632             if (halPropValue.getStatus() != VehiclePropertyStatus.AVAILABLE) {
633                 return clientRequestInfo.toErrorResult(
634                         new CarPropertyErrorCodes(
635                                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR,
636                                 /* vendorErrorCode= */ 0,
637                                 /* systemErrorCode= */ 0));
638             }
639 
640             try {
641                 return clientRequestInfo.toGetValueResult(
642                         halPropValue.toCarPropertyValue(mgrPropId, halPropConfig));
643             } catch (IllegalStateException e) {
644                 Slogf.e(TAG, e,
645                         "Cannot convert halPropValue to carPropertyValue, property: %s, areaId: %d",
646                         halPropIdToName(halPropValue.getPropId()), halPropValue.getAreaId());
647                 return clientRequestInfo.toErrorResult(
648                         new CarPropertyErrorCodes(
649                                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR,
650                                 /* vendorErrorCode= */ 0,
651                                 /* systemErrorCode= */ 0));
652             }
653         }
654 
655         @Override
onGetAsyncResults( List<GetVehicleStubAsyncResult> getVehicleStubAsyncResults)656         public void onGetAsyncResults(
657                 List<GetVehicleStubAsyncResult> getVehicleStubAsyncResults) {
658             List<GetSetValueResultWrapper> getValueResults = new ArrayList<>();
659             // If we receive get value result for initial value request and the result is the
660             // same as the target value, we might finish the associated async set value request.
661             // So we need potential set value results here.
662             List<GetSetValueResultWrapper> setValueResults = new ArrayList<>();
663             List<AsyncPropRequestInfo> retryRequests = new ArrayList<>();
664             synchronized (mLock) {
665                 for (int i = 0; i < getVehicleStubAsyncResults.size(); i++) {
666                     GetVehicleStubAsyncResult getVehicleStubAsyncResult =
667                             getVehicleStubAsyncResults.get(i);
668                     int serviceRequestId = getVehicleStubAsyncResult.getServiceRequestId();
669                     AsyncPropRequestInfo clientRequestInfo =
670                             getAndRemovePendingAsyncPropRequestInfoLocked(serviceRequestId);
671                     if (clientRequestInfo == null) {
672                         Slogf.w(TAG, "async request for ID: %d not found, ignore the result",
673                                 serviceRequestId);
674                         continue;
675                     }
676 
677                     int carPropMgrErrorCode = getVehicleStubAsyncResult.getErrorCode();
678                     if (carPropMgrErrorCode == CarPropertyErrorCodes.STATUS_TRY_AGAIN) {
679                         // The request might need to be retried.
680                         if (DBG) {
681                             Slogf.d(TAG, "request: %s try again", clientRequestInfo);
682                         }
683                         retryRequests.add(clientRequestInfo);
684                         continue;
685                     }
686 
687                     GetSetValueResult result = parseGetAsyncResults(getVehicleStubAsyncResult,
688                             clientRequestInfo);
689                     CarPropertyValue carPropertyValue = result.getCarPropertyValue();
690                     if (clientRequestInfo.getRequestType() != GET_INITIAL_VALUE_FOR_SET) {
691                         getValueResults.add(new GetSetValueResultWrapper(result,
692                                 clientRequestInfo.getAsyncRequestStartTime(),
693                                 clientRequestInfo.getRetryCount()));
694                         if (carPropertyValue != null) {
695                             int propertyId = carPropertyValue.getPropertyId();
696                             int areaId = carPropertyValue.getAreaId();
697                             if (isStaticAndSystemPropertyLocked(propertyId)) {
698                                 mStaticPropertyIdAreaIdCache.put(propertyId, areaId,
699                                         carPropertyValue);
700                             }
701                         }
702                         continue;
703                     }
704 
705                     if (DBG) {
706                         Slogf.d(TAG, "handling init value result for request: %s",
707                                 clientRequestInfo);
708                     }
709                     // Handle GET_INITIAL_VALUE_FOR_SET result.
710                     int errorCode = result.getCarPropertyErrorCodes()
711                             .getCarPropertyManagerErrorCode();
712                     if (errorCode != STATUS_OK) {
713                         Slogf.w(TAG, "the init value get request: %s failed, ignore the result, "
714                                 + "error: %d", clientRequestInfo, errorCode);
715                         continue;
716                     }
717                     // If the initial value result is the target value and the async set
718                     // request returned, we finish the pending async set result.
719                     AsyncPropRequestInfo assocSetValueRequestInfo =
720                             clientRequestInfo.getAssocSetValueRequestInfo();
721                     if (assocSetValueRequestInfo == null) {
722                         Slogf.e(TAG, "received get initial value result, but no associated set "
723                                 + "value request is defined");
724                         continue;
725                     }
726                     GetSetValueResult maybeSetResult = maybeFinishPendingSetValueRequestLocked(
727                             assocSetValueRequestInfo, carPropertyValue);
728                     if (maybeSetResult != null) {
729                         if (DBG) {
730                             Slogf.d(TAG, "The initial value is the same as target value for "
731                                     + "request: %s, sending success set result",
732                                     assocSetValueRequestInfo);
733                         }
734                         setValueResults.add(new GetSetValueResultWrapper(maybeSetResult,
735                                 assocSetValueRequestInfo.getAsyncRequestStartTime(),
736                                 assocSetValueRequestInfo.getRetryCount()));
737                         removePendingAsyncPropRequestInfoLocked(assocSetValueRequestInfo);
738                     }
739                 }
740                 updateSubscriptionRateForAsyncSetRequestLocked();
741             }
742 
743             sendGetValueResults(getValueResults);
744             sendSetValueResults(setValueResults);
745 
746             if (!retryRequests.isEmpty()) {
747                 mHandler.postDelayed(() -> {
748                     retryIfNotExpired(retryRequests);
749                 }, ASYNC_RETRY_SLEEP_IN_MS);
750             }
751         }
752 
753         @Override
onSetAsyncResults( List<SetVehicleStubAsyncResult> setVehicleStubAsyncResults)754         public void onSetAsyncResults(
755                 List<SetVehicleStubAsyncResult> setVehicleStubAsyncResults) {
756             List<GetSetValueResultWrapper> setValueResults = new ArrayList<>();
757             List<AsyncPropRequestInfo> retryRequests = new ArrayList<>();
758             synchronized (mLock) {
759                 for (int i = 0; i < setVehicleStubAsyncResults.size(); i++) {
760                     SetVehicleStubAsyncResult setVehicleStubAsyncResult =
761                             setVehicleStubAsyncResults.get(i);
762                     int serviceRequestId = setVehicleStubAsyncResult.getServiceRequestId();
763                     AsyncPropRequestInfo clientRequestInfo =
764                             getPendingAsyncPropRequestInfoLocked(serviceRequestId);
765                     if (clientRequestInfo == null) {
766                         Slogf.w(TAG, "async request for ID:  %d not found, ignore the result",
767                                 serviceRequestId);
768                         continue;
769                     }
770                     int carPropMgrErrorCode = setVehicleStubAsyncResult.getErrorCode();
771 
772                     if (carPropMgrErrorCode == CarPropertyErrorCodes.STATUS_TRY_AGAIN) {
773                         // The request might need to be retried.
774                         retryRequests.add(clientRequestInfo);
775                         removePendingAsyncPropRequestInfoLocked(clientRequestInfo);
776                         continue;
777                     }
778 
779                     if (carPropMgrErrorCode != STATUS_OK) {
780                         // All other error results will be delivered back through callback.
781                         setValueResults.add(new GetSetValueResultWrapper(clientRequestInfo
782                                 .toErrorResult(
783                                         setVehicleStubAsyncResult.getCarPropertyErrorCodes()),
784                                 clientRequestInfo.getAsyncRequestStartTime(),
785                                 clientRequestInfo.getRetryCount()));
786                         removePendingAsyncPropRequestInfoLocked(clientRequestInfo);
787                         mSetAsyncEndToEndLatencyHistogram
788                                 .logSample((float) System.currentTimeMillis()
789                                 - clientRequestInfo.getAsyncRequestStartTime());
790                         continue;
791                     }
792 
793                     clientRequestInfo.setSetRequestSent();
794                     if (clientRequestInfo.success()) {
795                         // If we have already received event for the target value or the initial
796                         // value is already the target value. Mark the request as complete.
797                         removePendingAsyncPropRequestInfoLocked(clientRequestInfo);
798                         // If we don't wait for property update event, then we don't know when
799                         // the property is updated to the target value. We set it to the
800                         // current timestamp.
801                         long updateTimestampNanos = clientRequestInfo.isWaitForPropertyUpdate()
802                                 ? clientRequestInfo.getUpdateTimestampNanos() :
803                                 SystemClock.elapsedRealtimeNanos();
804                         setValueResults.add(new GetSetValueResultWrapper(clientRequestInfo
805                                 .toSetValueResult(updateTimestampNanos),
806                                 clientRequestInfo.getAsyncRequestStartTime(),
807                                 clientRequestInfo.getRetryCount()));
808                     }
809                 }
810                 updateSubscriptionRateForAsyncSetRequestLocked();
811             }
812 
813             sendSetValueResults(setValueResults);
814 
815             if (!retryRequests.isEmpty()) {
816                 mHandler.postDelayed(() -> {
817                     retryIfNotExpired(retryRequests);
818                 }, ASYNC_RETRY_SLEEP_IN_MS);
819             }
820         }
821 
generateTimeoutResult(AsyncPropRequestInfo requestInfo, List<GetSetValueResultWrapper> timeoutGetResults, List<GetSetValueResultWrapper> timeoutSetResults)822         private void generateTimeoutResult(AsyncPropRequestInfo requestInfo,
823                 List<GetSetValueResultWrapper> timeoutGetResults,
824                 List<GetSetValueResultWrapper> timeoutSetResults) {
825             GetSetValueResult timeoutResult =  requestInfo.toErrorResult(
826                     new CarPropertyErrorCodes(
827                             CarPropertyManager.STATUS_ERROR_TIMEOUT,
828                             /* vendorErrorCode= */ 0,
829                             /* systemErrorCode= */ 0));
830             Slogf.w(TAG, "the %s request for request ID: %d time out, request time: %d ms, current"
831                     + " time: %d ms", requestTypeToString(requestInfo.getRequestType()),
832                     requestInfo.getRequestId(), requestInfo.getAsyncRequestStartTime(),
833                     System.currentTimeMillis());
834 
835             switch (requestInfo.getRequestType()) {
836                 case GET:
837                     timeoutGetResults.add(new GetSetValueResultWrapper(timeoutResult,
838                             requestInfo.getAsyncRequestStartTime(), requestInfo.getRetryCount()));
839                     break;
840                 case GET_INITIAL_VALUE_FOR_SET:
841                     // Do not send the timeout requests back to the user because the original
842                     // request is not originated from the user.
843                     Slogf.e(TAG, "the initial value request: %s timeout", requestInfo);
844                     break;
845                 case SET:
846                     timeoutSetResults.add(new GetSetValueResultWrapper(timeoutResult,
847                             requestInfo.getAsyncRequestStartTime(), requestInfo.getRetryCount()));
848                     break;
849             }
850         }
851 
852         @Override
onRequestsTimeout(List<Integer> serviceRequestIds)853         public void onRequestsTimeout(List<Integer> serviceRequestIds) {
854             List<GetSetValueResultWrapper> timeoutGetResults = new ArrayList<>();
855             List<GetSetValueResultWrapper> timeoutSetResults = new ArrayList<>();
856             synchronized (mLock) {
857                 for (int i = 0; i < serviceRequestIds.size(); i++) {
858                     int serviceRequestId = serviceRequestIds.get(i);
859                     AsyncPropRequestInfo requestInfo =
860                             getAndRemovePendingAsyncPropRequestInfoLocked(serviceRequestId);
861                     if (requestInfo == null) {
862                         Slogf.w(TAG, "Service request ID %d time out but no "
863                                 + "pending request is found. The request may have already been "
864                                 + "cancelled or finished", serviceRequestId);
865                         continue;
866                     }
867                     if (DBG) {
868                         Slogf.d(TAG, "Request: %s time out", requestInfo);
869                     }
870                     generateTimeoutResult(requestInfo, timeoutGetResults, timeoutSetResults);
871                 }
872                 updateSubscriptionRateForAsyncSetRequestLocked();
873             }
874             sendGetValueResults(timeoutGetResults);
875             sendSetValueResults(timeoutSetResults);
876         }
877     }
878 
879     /**
880      * Maybe finish the pending set value request depending on the updated value.
881      *
882      * Check whether the updated property value is the same as the target value for pending
883      * set value requests. If so, finish those requests.
884      *
885      * @return A success set value result for the finished request or {@code null}.
886      */
887     @GuardedBy("mLock")
888     @Nullable
maybeFinishPendingSetValueRequestLocked( AsyncPropRequestInfo pendingSetValueRequest, CarPropertyValue updatedValue)889     private GetSetValueResult maybeFinishPendingSetValueRequestLocked(
890             AsyncPropRequestInfo pendingSetValueRequest, CarPropertyValue updatedValue) {
891         Object targetValue = pendingSetValueRequest.getPropSvcRequest()
892                 .getCarPropertyValue().getValue();
893         Object currentValue = updatedValue.getValue();
894         if (!targetValue.equals(currentValue)) {
895             if (DBG) {
896                 Slogf.d(TAG, "Async set value request: %s receive different updated value: %s"
897                         + " than target value: %s", pendingSetValueRequest, currentValue,
898                         targetValue);
899             }
900             return null;
901         }
902         long updateTimestampNanos = updatedValue.getTimestamp();
903         pendingSetValueRequest.setValueUpdated(updateTimestampNanos);
904         if (!pendingSetValueRequest.success()) {
905             return null;
906         }
907 
908         return pendingSetValueRequest.toSetValueResult(updateTimestampNanos);
909     }
910 
911     /**
912      * Generates a {@link AsyncGetSetRequest} according to a {@link AsyncPropRequestInfo}.
913      *
914      * <p>Generates a new PropertyHalService Request ID. Associate the ID with the request and
915      * returns a {@link AsyncGetSetRequest} that could be sent to {@link VehicleStub}.
916      */
917     @GuardedBy("mLock")
generateVehicleStubAsyncRequestLocked( AsyncPropRequestInfo asyncPropRequestInfo)918     private AsyncGetSetRequest generateVehicleStubAsyncRequestLocked(
919             AsyncPropRequestInfo asyncPropRequestInfo) {
920         int serviceRequestId = mServiceRequestIdCounter.getAndIncrement();
921         asyncPropRequestInfo.setServiceRequestId(serviceRequestId);
922 
923         HalPropValue halPropValue;
924         CarPropertyValue requestCarPropertyValue = asyncPropRequestInfo.getPropSvcRequest()
925                 .getCarPropertyValue();
926         if (requestCarPropertyValue != null) {
927             // If this is a set request, the car property value stores the value to be set.
928             halPropValue = carPropertyValueToHalPropValueLocked(requestCarPropertyValue);
929         } else {
930             // Otherwise this is a get request, we only need the property ID and area ID.
931             int halPropertyId = managerToHalPropId(asyncPropRequestInfo.getPropertyId());
932             int areaId = asyncPropRequestInfo.getAreaId();
933             halPropValue = mPropValueBuilder.build(halPropertyId, areaId);
934         }
935         return new AsyncGetSetRequest(serviceRequestId, halPropValue,
936                 asyncPropRequestInfo.getTimeoutUptimeMs());
937     }
938 
939     @GuardedBy("mLock")
getPendingAsyncPropRequestInfoLocked( int serviceRequestId)940     @Nullable private AsyncPropRequestInfo getPendingAsyncPropRequestInfoLocked(
941             int serviceRequestId) {
942         AsyncPropRequestInfo requestInfo =
943                 mPendingAsyncRequests.getRequestIfFound(serviceRequestId);
944         if (requestInfo == null) {
945             Slogf.w(TAG, "the request for propertyHalService request "
946                     + "ID: %d already timed out or already completed", serviceRequestId);
947         }
948         return requestInfo;
949     }
950 
951     @GuardedBy("mLock")
getAndRemovePendingAsyncPropRequestInfoLocked( int serviceRequestId)952     @Nullable private AsyncPropRequestInfo getAndRemovePendingAsyncPropRequestInfoLocked(
953             int serviceRequestId) {
954         AsyncPropRequestInfo requestInfo = getPendingAsyncPropRequestInfoLocked(serviceRequestId);
955         if (requestInfo == null) {
956             Slogf.w(TAG, "the request for propertyHalService request "
957                     + "ID: %d already timed out or already completed", serviceRequestId);
958             return null;
959         }
960         removePendingAsyncPropRequestInfoLocked(requestInfo);
961         return requestInfo;
962     }
963 
964     /**
965      * Remove the pending async request from the pool.
966      *
967      * If the request to remove is an async set request, also remove it from the
968      * {@code mHalPropIdToWaitingUpdateRequestInfo} map. This will cause the subscription rate to
969      * be updated for the specific property because we no longer need to monitor this property
970      * any more internally.
971      *
972      * The {@code updatedAreaIdsByHalPropIds} will store the affected area Ids and property IDs if
973      * their subscription rate need to be recalculated.
974      */
975     @GuardedBy("mLock")
removePendingAsyncPropRequestInfoLocked(AsyncPropRequestInfo pendingRequest)976     private void removePendingAsyncPropRequestInfoLocked(AsyncPropRequestInfo pendingRequest) {
977         int serviceRequestId = pendingRequest.getServiceRequestId();
978         mPendingAsyncRequests.removeRequest(serviceRequestId);
979         if (pendingRequest.getRequestType() == SET) {
980             cleanupPendingAsyncSetRequestLocked(pendingRequest);
981         }
982     }
983 
984     @GuardedBy("mLock")
cleanupPendingAsyncSetRequestLocked(AsyncPropRequestInfo pendingRequest)985     private void cleanupPendingAsyncSetRequestLocked(AsyncPropRequestInfo pendingRequest) {
986         int halPropId = managerToHalPropId(pendingRequest.getPropertyId());
987         if (!pendingRequest.isWaitForPropertyUpdate()) {
988             return;
989         }
990         if (pendingRequest.getAssocGetInitValueRequestInfo() == null) {
991             Slogf.e(TAG, "The pending async set value request: %s"
992                     + " does not have an associated get initial value request, must not happen",
993                     pendingRequest);
994             return;
995         }
996         // If we are removing an async set property request, then we should remove its associated
997         // get initial value request as well if it has not been finished.
998         AsyncPropRequestInfo assocGetInitValueRequestInfo =
999                 pendingRequest.getAssocGetInitValueRequestInfo();
1000         int assocInitValueRequestId = assocGetInitValueRequestInfo.getServiceRequestId();
1001         assocGetInitValueRequestInfo = mPendingAsyncRequests.getRequestIfFound(
1002                 assocInitValueRequestId);
1003         if (assocGetInitValueRequestInfo != null) {
1004             mPendingAsyncRequests.removeRequest(assocInitValueRequestId);
1005             // Use a separate runnable to do this outside lock.
1006             mHandler.post(() -> mVehicleHal.cancelRequests(List.of(assocInitValueRequestId)));
1007         }
1008         if (!mHalPropIdToWaitingUpdateRequestInfo.contains(halPropId)) {
1009             return;
1010         }
1011         if (!mHalPropIdToWaitingUpdateRequestInfo.get(halPropId).remove(pendingRequest)) {
1012             return;
1013         }
1014         if (mHalPropIdToWaitingUpdateRequestInfo.get(halPropId).isEmpty()) {
1015             mHalPropIdToWaitingUpdateRequestInfo.remove(halPropId);
1016         }
1017         // We no longer need to subscribe to the property.
1018         mSubManager.stageUnregister(new ClientType(pendingRequest.getServiceRequestId()),
1019                 new ArraySet<Integer>(Set.of(halPropId)));
1020     }
1021 
1022     /**
1023      * PropertyHalListener used to send events to CarPropertyService
1024      */
1025     public interface PropertyHalListener {
1026         /**
1027          * This event is sent whenever the property value is updated
1028          */
onPropertyChange(List<CarPropertyEvent> events)1029         void onPropertyChange(List<CarPropertyEvent> events);
1030 
1031         /**
1032          * This event is sent when the set property call fails
1033          */
onPropertySetError(int property, int area, @CarSetPropertyErrorCode int errorCode)1034         void onPropertySetError(int property, int area,
1035                 @CarSetPropertyErrorCode int errorCode);
1036 
1037     }
1038 
PropertyHalService(VehicleHal vehicleHal)1039     public PropertyHalService(VehicleHal vehicleHal) {
1040         mVehicleHal = vehicleHal;
1041         if (DBG) {
1042             Slogf.d(TAG, "started PropertyHalService");
1043         }
1044         mPropValueBuilder = vehicleHal.getHalPropValueBuilder();
1045     }
1046 
1047     /**
1048      * Set the listener for the HAL service
1049      */
setPropertyHalListener(PropertyHalListener propertyHalListener)1050     public void setPropertyHalListener(PropertyHalListener propertyHalListener) {
1051         synchronized (mLock) {
1052             mPropertyHalListener = propertyHalListener;
1053         }
1054     }
1055 
1056     /**
1057      * Used for resetting the configs state during unit testing. The real implementation uses a
1058      * static instance of configs so one test will affect the state of another.
1059      */
1060     @VisibleForTesting
setPropertyHalServiceConfigs(PropertyHalServiceConfigs configs)1061     void setPropertyHalServiceConfigs(PropertyHalServiceConfigs configs) {
1062         mPropertyHalServiceConfigs = configs;
1063     }
1064 
1065     /**
1066      * @return SparseArray<CarPropertyConfig> List of configs available.
1067      */
getPropertyList()1068     public SparseArray<CarPropertyConfig<?>> getPropertyList() {
1069         if (DBG) {
1070             Slogf.d(TAG, "getPropertyList");
1071         }
1072         synchronized (mLock) {
1073             SparseArray<CarPropertyConfig<?>> mgrPropIdToCarPropConfig = new SparseArray<>();
1074             for (int i = 0; i < mHalPropIdToPropConfig.size(); i++) {
1075                 HalPropConfig halPropConfig = mHalPropIdToPropConfig.valueAt(i);
1076                 int mgrPropId = halToManagerPropId(halPropConfig.getPropId());
1077                 CarPropertyConfig<?> carPropertyConfig = halPropConfig.toCarPropertyConfig(
1078                         mgrPropId, mPropertyHalServiceConfigs);
1079                 mgrPropIdToCarPropConfig.put(mgrPropId, carPropertyConfig);
1080             }
1081             return mgrPropIdToCarPropConfig;
1082         }
1083     }
1084 
1085     /**
1086      * Returns property value.
1087      *
1088      * @param mgrPropId property id in {@link VehiclePropertyIds}
1089      * @throws IllegalArgumentException if argument is not valid.
1090      * @throws ServiceSpecificException if there is an exception in HAL or the property status is
1091      *                                  not available.
1092      */
getProperty(int mgrPropId, int areaId)1093     public CarPropertyValue getProperty(int mgrPropId, int areaId)
1094             throws IllegalArgumentException, ServiceSpecificException {
1095         int halPropId = managerToHalPropId(mgrPropId);
1096         // CarPropertyManager catches and rethrows exception, no need to handle here.
1097         HalPropValue halPropValue;
1098         HalPropConfig halPropConfig;
1099         synchronized (mLock) {
1100             halPropConfig = mHalPropIdToPropConfig.get(halPropId);
1101             if (isStaticAndSystemPropertyLocked(mgrPropId)) {
1102                 CarPropertyValue carPropertyValue = mStaticPropertyIdAreaIdCache.get(mgrPropId,
1103                         areaId);
1104                 if (carPropertyValue != null) {
1105                     if (DBG) {
1106                         Slogf.d(TAG, "Get Sync Property: %s retrieved from cache",
1107                                 VehiclePropertyIds.toString(mgrPropId));
1108                     }
1109                     return carPropertyValue;
1110                 }
1111             }
1112         }
1113         halPropValue = mVehicleHal.get(halPropId, areaId);
1114         try {
1115             CarPropertyValue result = halPropValue.toCarPropertyValue(mgrPropId, halPropConfig);
1116             synchronized (mLock) {
1117                 if (!isStaticAndSystemPropertyLocked(mgrPropId)) {
1118                     return result;
1119                 }
1120                 mStaticPropertyIdAreaIdCache.put(mgrPropId, areaId, result);
1121                 return result;
1122             }
1123         } catch (IllegalStateException e) {
1124             throw new ServiceSpecificException(STATUS_INTERNAL_ERROR,
1125                     "Cannot convert halPropValue to carPropertyValue, property: "
1126                     + VehiclePropertyIds.toString(mgrPropId) + " areaId: " + areaId
1127                     + ", exception: " + e);
1128         }
1129     }
1130 
1131     /**
1132      * Get the read permission string for the property. The format of the return value of this
1133      * function has changed over time and thus should not be relied on.
1134      */
1135     @Nullable
getReadPermission(int mgrPropId)1136     public String getReadPermission(int mgrPropId) {
1137         PermissionCondition readPermission =
1138                 mPropertyHalServiceConfigs.getReadPermission(managerToHalPropId(mgrPropId));
1139         if (readPermission == null) {
1140             Slogf.w(TAG, "readPermission is null for mgrPropId: "
1141                     + VehiclePropertyIds.toString(mgrPropId));
1142             return null;
1143         }
1144         return readPermission.toString();
1145     }
1146 
1147     /**
1148      * Get the write permission string for the property. The format of the return value of this
1149      * function has changed over time and thus should not be relied on.
1150      */
1151     @Nullable
getWritePermission(int mgrPropId)1152     public String getWritePermission(int mgrPropId) {
1153         PermissionCondition writePermission =
1154                 mPropertyHalServiceConfigs.getWritePermission(managerToHalPropId(mgrPropId));
1155         if (writePermission == null) {
1156             Slogf.w(TAG, "writePermission is null for mgrPropId: "
1157                     + VehiclePropertyIds.toString(mgrPropId));
1158             return null;
1159         }
1160         return writePermission.toString();
1161     }
1162 
1163     /**
1164      * Checks whether the property is readable.
1165      */
isReadable(Context context, int mgrPropId)1166     public boolean isReadable(Context context, int mgrPropId) {
1167         return mPropertyHalServiceConfigs.isReadable(context, managerToHalPropId(mgrPropId));
1168     }
1169 
1170     /**
1171      * Checks whether the property is writable.
1172      */
isWritable(Context context, int mgrPropId)1173     public boolean isWritable(Context context, int mgrPropId) {
1174         return mPropertyHalServiceConfigs.isWritable(context, managerToHalPropId(mgrPropId));
1175     }
1176 
1177     /**
1178      * Set the property value.
1179      *
1180      * @throws IllegalArgumentException if argument is invalid.
1181      * @throws ServiceSpecificException if there is an exception in HAL.
1182      */
setProperty(CarPropertyValue carPropertyValue)1183     public void setProperty(CarPropertyValue carPropertyValue)
1184             throws IllegalArgumentException, ServiceSpecificException {
1185         HalPropValue valueToSet;
1186         synchronized (mLock) {
1187             valueToSet = carPropertyValueToHalPropValueLocked(carPropertyValue);
1188         }
1189 
1190         // CarPropertyManager catches and rethrows exception, no need to handle here.
1191         mVehicleHal.set(valueToSet);
1192     }
1193 
1194     /**
1195      * Subscribe to this property at the specified updateRateHz and areaId. The list of
1196      * carSubscriptions should never be empty since it is checked at CarPropertyService.
1197      *
1198      * @throws ServiceSpecificException If VHAL returns error.
1199      */
subscribeProperty(List<CarSubscription> carSubscriptions)1200     public void subscribeProperty(List<CarSubscription> carSubscriptions)
1201             throws ServiceSpecificException {
1202         synchronized (mLock) {
1203             // Even though this involves binder call, this must be done inside the lock so that
1204             // the state in {@code mSubManager} is consistent with the state in VHAL.
1205             for (int i = 0; i < carSubscriptions.size(); i++) {
1206                 CarSubscription carSubscription = carSubscriptions.get(i);
1207                 int mgrPropId = carSubscription.propertyId;
1208                 int[] areaIds = carSubscription.areaIds;
1209                 float updateRateHz = carSubscription.updateRateHz;
1210                 if (DBG) {
1211                     Slogf.d(TAG, "subscribeProperty propertyId: %s, updateRateHz=%f",
1212                             VehiclePropertyIds.toString(mgrPropId), updateRateHz);
1213                 }
1214                 int halPropId = managerToHalPropId(mgrPropId);
1215                 // Note that we use halPropId instead of mgrPropId in mSubManager.
1216                 mSubManager.stageNewOptions(new ClientType(CAR_PROP_SVC_REQUEST_ID),
1217                         List.of(newCarSubscription(halPropId, areaIds, updateRateHz,
1218                         carSubscription.enableVariableUpdateRate, carSubscription.resolution)));
1219             }
1220             try {
1221                 updateSubscriptionRateLocked();
1222             } catch (ServiceSpecificException e) {
1223                 Slogf.e(TAG, "Failed to update subscription rate for subscribe", e);
1224                 throw e;
1225             }
1226         }
1227     }
1228 
1229     /**
1230      * Unsubscribe the property and turn off update events for it.
1231      *
1232      * @throws ServiceSpecificException If VHAL returns error.
1233      */
unsubscribeProperty(int mgrPropId)1234     public void unsubscribeProperty(int mgrPropId) throws ServiceSpecificException {
1235         if (DBG) {
1236             Slogf.d(TAG, "unsubscribeProperty mgrPropId=%s",
1237                     VehiclePropertyIds.toString(mgrPropId));
1238         }
1239         int halPropId = managerToHalPropId(mgrPropId);
1240         synchronized (mLock) {
1241             // Even though this involves binder call, this must be done inside the lock so that
1242             // the state in {@code mSubManager} is consistent with the state in VHAL.
1243             mSubManager.stageUnregister(new ClientType(CAR_PROP_SVC_REQUEST_ID),
1244                     new ArraySet<Integer>(Set.of(halPropId)));
1245             try {
1246                 updateSubscriptionRateLocked();
1247             } catch (ServiceSpecificException e) {
1248                 Slogf.e(TAG, "Failed to update subscription rate for unsubscribe, "
1249                         + "restoring previous state", e);
1250                 throw e;
1251             }
1252         }
1253     }
1254 
1255     @Override
init()1256     public void init() {
1257         if (DBG) {
1258             Slogf.d(TAG, "init()");
1259         }
1260     }
1261 
1262     @Override
release()1263     public void release() {
1264         if (DBG) {
1265             Slogf.d(TAG, "release()");
1266         }
1267         synchronized (mLock) {
1268             ArraySet<Integer> halPropIds = mSubManager.getCurrentSubscribedPropIds();
1269             for (int i = 0; i < halPropIds.size(); i++) {
1270                 int halPropId = halPropIds.valueAt(i);
1271                 mVehicleHal.unsubscribePropertySafe(this, halPropId);
1272             }
1273             mSubManager.clear();
1274             mHalPropIdToPropConfig.clear();
1275             mPropertyHalListener = null;
1276         }
1277         mHandlerThread.quitSafely();
1278     }
1279 
1280     @Override
isSupportedProperty(int halPropId)1281     public boolean isSupportedProperty(int halPropId) {
1282         return mPropertyHalServiceConfigs.isSupportedProperty(halPropId)
1283                 && CarPropertyHelper.isSupported(halToManagerPropId(halPropId));
1284     }
1285 
1286     @Override
getAllSupportedProperties()1287     public int[] getAllSupportedProperties() {
1288         return EMPTY_INT_ARRAY;
1289     }
1290 
1291     // The method is called in HAL init(). Avoid handling complex things in here.
1292     @Override
takeProperties(Collection<HalPropConfig> halPropConfigs)1293     public void takeProperties(Collection<HalPropConfig> halPropConfigs) {
1294         for (HalPropConfig halPropConfig : halPropConfigs) {
1295             int halPropId = halPropConfig.getPropId();
1296             if (isSupportedProperty(halPropId)) {
1297                 synchronized (mLock) {
1298                     mHalPropIdToPropConfig.put(halPropId, halPropConfig);
1299                 }
1300                 if (DBG) {
1301                     Slogf.d(TAG, "takeSupportedProperties: %s", halPropIdToName(halPropId));
1302                 }
1303             } else {
1304                 if (DBG) {
1305                     Slogf.d(TAG, "takeProperties: Property: %s is not supported, ignore",
1306                             halPropIdToName(halPropId));
1307                 }
1308             }
1309         }
1310         if (DBG) {
1311             Slogf.d(TAG, "takeSupportedProperties() took %d properties", halPropConfigs.size());
1312         }
1313         // If vehicle hal support to select permission for vendor properties.
1314         HalPropConfig customizePermission = mVehicleHal.getPropConfig(
1315                 VehicleProperty.SUPPORT_CUSTOMIZE_VENDOR_PERMISSION);
1316         if (customizePermission != null) {
1317             mPropertyHalServiceConfigs.customizeVendorPermission(
1318                     customizePermission.getConfigArray());
1319         } else {
1320             if (DBG) {
1321                 Slogf.d(TAG, "No custom vendor permission defined in VHAL");
1322             }
1323         }
1324     }
1325 
storeResultForRequest(GetSetValueResult result, AsyncPropRequestInfo request, Map<VehicleStubCallback, List<GetSetValueResultWrapper>> callbackToResults)1326     private static void storeResultForRequest(GetSetValueResult result,
1327             AsyncPropRequestInfo request,
1328             Map<VehicleStubCallback, List<GetSetValueResultWrapper>> callbackToResults) {
1329         VehicleStubCallback clientCallback = request.getVehicleStubCallback();
1330         if (callbackToResults.get(clientCallback) == null) {
1331             callbackToResults.put(clientCallback, new ArrayList<>());
1332         }
1333         callbackToResults.get(clientCallback).add(new GetSetValueResultWrapper(result,
1334                 request.getAsyncRequestStartTime(), request.getRetryCount()));
1335     }
1336 
1337     /**
1338      * Check whether there is pending async set value request for the property.
1339      *
1340      * If there are pending async set value request, check whether the updated property value is
1341      * the target value. If so, store the success set value result into callbackToSetValueResults.
1342      */
1343     @GuardedBy("mLock")
checkPendingWaitForUpdateRequestsLocked(int halPropId, CarPropertyValue<?> updatedValue, Map<VehicleStubCallback, List<GetSetValueResultWrapper>> callbackToSetValueResults)1344     private void checkPendingWaitForUpdateRequestsLocked(int halPropId,
1345             CarPropertyValue<?> updatedValue,
1346             Map<VehicleStubCallback, List<GetSetValueResultWrapper>> callbackToSetValueResults) {
1347         List<AsyncPropRequestInfo> pendingSetRequests = mHalPropIdToWaitingUpdateRequestInfo.get(
1348                 halPropId);
1349         if (pendingSetRequests == null) {
1350             return;
1351         }
1352         List<AsyncPropRequestInfo> finishedPendingSetRequests = new ArrayList<>();
1353         for (AsyncPropRequestInfo pendingSetRequest : pendingSetRequests) {
1354             GetSetValueResult maybeSetResult = maybeFinishPendingSetValueRequestLocked(
1355                     pendingSetRequest, updatedValue);
1356             if (pendingSetRequest.getAreaId() != updatedValue.getAreaId()) {
1357                 continue;
1358             }
1359             // Don't remove the finished pending request info during the loop since it will
1360             // modify pendingSetRequests array.
1361             if (maybeSetResult == null) {
1362                 if (DBG) {
1363                     Slogf.d(TAG, "received property update event for request: %s, but the value is "
1364                             + "different than target value", pendingSetRequest);
1365                 }
1366                 continue;
1367             }
1368             if (DBG) {
1369                 Slogf.d(TAG, "received property update to target value event for request: %s"
1370                         + ", sending success async set value result", pendingSetRequest);
1371             }
1372             storeResultForRequest(maybeSetResult, pendingSetRequest, callbackToSetValueResults);
1373             finishedPendingSetRequests.add(pendingSetRequest);
1374         }
1375 
1376         for (AsyncPropRequestInfo finishedRequest : finishedPendingSetRequests) {
1377             // Pending set value request is now succeeded. Remove all record to the pending request.
1378             removePendingAsyncPropRequestInfoLocked(finishedRequest);
1379         }
1380     }
1381 
toHalSubscribeOptions( ArrayList<CarSubscription> carSubscriptions)1382     private static ArrayList<HalSubscribeOptions> toHalSubscribeOptions(
1383             ArrayList<CarSubscription> carSubscriptions) {
1384         ArrayList<HalSubscribeOptions> halOptions = new ArrayList<>();
1385         for (int i = 0; i < carSubscriptions.size(); i++) {
1386             CarSubscription carOption = carSubscriptions.get(i);
1387             halOptions.add(new HalSubscribeOptions(carOption.propertyId, carOption.areaIds,
1388                     carOption.updateRateHz, carOption.enableVariableUpdateRate,
1389                     carOption.resolution));
1390         }
1391         return halOptions;
1392     }
1393 
1394     /**
1395      * Apply the staged subscription rate in {@code mSubManager} to VHAL.
1396      *
1397      * Use {@code subscribeProperty} to update its subscription rate or {@code unsubscribeProperty}
1398      * if it is no longer subscribed.
1399      *
1400      * This functions involves binder call to VHAL, but we intentionally keep this inside the
1401      * lock because we need to keep the subscription status consistent. If we do not use lock
1402      * here, the following situation might happen:
1403      *
1404      * <ol>
1405      * <li>Lock is obtained by thread 1.
1406      * <li>mSubManager is updated by one thread to state 1.
1407      * <li>New update rate (local variable) is calculated based on state 1.
1408      * <li>Lock is released by thread 1.
1409      * <li>Lock is obtained by thread 2.
1410      * <li>mSubManager is updated by thread 2 to state 2.
1411      * <li>New update rate (local variable) is calculated based on state 2.
1412      * <li>Lock is released by thread 2.
1413      * <li>Thread 2 calls subscribeProperty to VHAL based on state 2.
1414      * <li>Thread 1 calls subscribeProperty to VHAL based on state 1.
1415      * <li>Now internally, the state is in state 2, but from VHAL side, it is in state 1.
1416      */
1417     @GuardedBy("mLock")
updateSubscriptionRateLocked()1418     private void updateSubscriptionRateLocked() throws ServiceSpecificException {
1419         ArrayList<CarSubscription> diffSubscribeOptions = new ArrayList<>();
1420         List<Integer> propIdsToUnsubscribe = new ArrayList<>();
1421         mSubManager.diffBetweenCurrentAndStage(diffSubscribeOptions, propIdsToUnsubscribe);
1422         try {
1423             if (!diffSubscribeOptions.isEmpty()) {
1424                 if (DBG) {
1425                     Slogf.d(TAG, "subscribeProperty, options: %s", diffSubscribeOptions);
1426                 }
1427                 // This may throw ServiceSpecificException.
1428                 mVehicleHal.subscribeProperty(this, toHalSubscribeOptions(diffSubscribeOptions));
1429             }
1430             for (int halPropId : propIdsToUnsubscribe) {
1431                 if (DBG) {
1432                     Slogf.d(TAG, "unsubscribeProperty for property ID: %s",
1433                             halPropIdToName(halPropId));
1434                 }
1435                 // This may throw ServiceSpecificException.
1436                 mVehicleHal.unsubscribeProperty(this, halPropId);
1437             }
1438             mSubManager.commit();
1439         } catch (IllegalArgumentException e) {
1440             Slogf.e(TAG, "Failed to subscribe/unsubscribe, property is not supported, this should "
1441                     + "not happen, caller must make sure the property is supported", e);
1442             mSubManager.dropCommit();
1443             return;
1444         } catch (Exception e) {
1445             mSubManager.dropCommit();
1446             throw e;
1447         }
1448     }
1449 
1450     @GuardedBy("mLock")
updateSubscriptionRateForAsyncSetRequestLocked()1451     private void updateSubscriptionRateForAsyncSetRequestLocked() {
1452         try {
1453             updateSubscriptionRateLocked();
1454         } catch (ServiceSpecificException e) {
1455             Slogf.e(TAG, "failed to update subscription rate after we finish async set request", e);
1456             return;
1457         }
1458     }
1459 
1460     @Override
onHalEvents(List<HalPropValue> halPropValues)1461     public void onHalEvents(List<HalPropValue> halPropValues) {
1462 
1463         List<CarPropertyEvent> eventsToDispatch = new ArrayList<>();
1464 
1465         // A map to store potential succeeded set value results which is caused by the values
1466         // updated to the target values.
1467         Map<VehicleStubCallback, List<GetSetValueResultWrapper>> callbackToSetValueResults =
1468                 new ArrayMap<>();
1469 
1470         synchronized (mLock) {
1471             for (HalPropValue halPropValue : halPropValues) {
1472                 if (halPropValue == null) {
1473                     continue;
1474                 }
1475                 int halPropId = halPropValue.getPropId();
1476                 HalPropConfig halPropConfig = mHalPropIdToPropConfig.get(halPropId);
1477                 if (halPropConfig == null) {
1478                     Slogf.w(TAG, "onHalEvents - received HalPropValue for unsupported property: %s",
1479                             halPropIdToName(halPropId));
1480                     continue;
1481                 }
1482                 // Check payload if it is an userdebug build.
1483                 if (BuildHelper.isDebuggableBuild()
1484                         && !mPropertyHalServiceConfigs.checkPayload(halPropValue)) {
1485                     Slogf.wtf(TAG,
1486                             "Drop event for property: %s because it is failed "
1487                                     + "in payload checking.", halPropValue);
1488                 }
1489                 int mgrPropId = halToManagerPropId(halPropId);
1490                 if (DBG && halPropValue.getStatus() != VehiclePropertyStatus.AVAILABLE) {
1491                     Slogf.d(TAG, "Received event %s with status that is not AVAILABLE",
1492                             halPropValue);
1493                 }
1494                 try {
1495                     CarPropertyValue<?> carPropertyValue = halPropValue.toCarPropertyValue(
1496                             mgrPropId, halPropConfig);
1497                     CarPropertyEvent carPropertyEvent = new CarPropertyEvent(
1498                             CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, carPropertyValue);
1499                     eventsToDispatch.add(carPropertyEvent);
1500 
1501                     checkPendingWaitForUpdateRequestsLocked(halPropId, carPropertyValue,
1502                             callbackToSetValueResults);
1503                 } catch (IllegalStateException e) {
1504                     Slogf.w(TAG, "Drop event %s that does not have valid value", halPropValue);
1505                     continue;
1506                 }
1507             }
1508             updateSubscriptionRateForAsyncSetRequestLocked();
1509         }
1510 
1511         PropertyHalListener propertyHalListener;
1512         synchronized (mLock) {
1513             propertyHalListener = mPropertyHalListener;
1514         }
1515         if (propertyHalListener != null) {
1516             propertyHalListener.onPropertyChange(eventsToDispatch);
1517         }
1518 
1519         for (VehicleStubCallback callback : callbackToSetValueResults.keySet()) {
1520             callback.sendSetValueResults(callbackToSetValueResults.get(callback));
1521         }
1522     }
1523 
convertStatusCodeToCarSetPropertyErrorCode( @ehicleHalStatusCodeInt int vhalStatusCode)1524     private static @CarSetPropertyErrorCode int convertStatusCodeToCarSetPropertyErrorCode(
1525             @VehicleHalStatusCodeInt int vhalStatusCode) {
1526         switch (vhalStatusCode) {
1527             case STATUS_TRY_AGAIN:
1528                 return CAR_SET_PROPERTY_ERROR_CODE_TRY_AGAIN;
1529             case STATUS_INVALID_ARG:
1530                 return CAR_SET_PROPERTY_ERROR_CODE_INVALID_ARG;
1531             case STATUS_NOT_AVAILABLE: // fallthrough
1532             case STATUS_NOT_AVAILABLE_DISABLED: // fallthrough
1533             case STATUS_NOT_AVAILABLE_SPEED_LOW: // fallthrough
1534             case STATUS_NOT_AVAILABLE_SPEED_HIGH: // fallthrough
1535             case STATUS_NOT_AVAILABLE_POOR_VISIBILITY: // fallthrough
1536             case STATUS_NOT_AVAILABLE_SAFETY:
1537                 return CAR_SET_PROPERTY_ERROR_CODE_PROPERTY_NOT_AVAILABLE;
1538             case STATUS_ACCESS_DENIED:
1539                 return CAR_SET_PROPERTY_ERROR_CODE_ACCESS_DENIED;
1540             default:
1541                 return CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN;
1542         }
1543     }
1544 
1545     @Override
onPropertySetError(ArrayList<VehiclePropError> vehiclePropErrors)1546     public void onPropertySetError(ArrayList<VehiclePropError> vehiclePropErrors) {
1547         PropertyHalListener propertyHalListener;
1548         synchronized (mLock) {
1549             propertyHalListener = mPropertyHalListener;
1550         }
1551         if (propertyHalListener != null) {
1552             for (int i = 0; i < vehiclePropErrors.size(); i++) {
1553                 VehiclePropError vehiclePropError = vehiclePropErrors.get(i);
1554                 int mgrPropId = halToManagerPropId(vehiclePropError.propId);
1555                 int vhalErrorCode = CarPropertyErrorCodes.getVhalSystemErrorCode(
1556                         vehiclePropError.errorCode);
1557                 Slogf.w(TAG,
1558                         "onPropertySetError for property: %s, area ID: %d, vhal error code: %d",
1559                         VehiclePropertyIds.toString(mgrPropId), vehiclePropError.areaId,
1560                         vhalErrorCode);
1561                 @CarSetPropertyErrorCode int carPropErrorCode =
1562                         convertStatusCodeToCarSetPropertyErrorCode(vhalErrorCode);
1563                 propertyHalListener.onPropertySetError(mgrPropId, vehiclePropError.areaId,
1564                         carPropErrorCode);
1565             }
1566         }
1567         Map<VehicleStubCallback, List<GetSetValueResultWrapper>> callbackToSetValueResults =
1568                 new ArrayMap<>();
1569         synchronized (mLock) {
1570             for (int i = 0; i < vehiclePropErrors.size(); i++) {
1571                 VehiclePropError vehiclePropError = vehiclePropErrors.get(i);
1572                 // Fail all pending async set requests that are currently waiting for property
1573                 // update which has the same property ID and same area ID.
1574                 int halPropId = vehiclePropError.propId;
1575                 List<AsyncPropRequestInfo> pendingSetRequests =
1576                         mHalPropIdToWaitingUpdateRequestInfo.get(halPropId);
1577                 if (pendingSetRequests == null) {
1578                     continue;
1579                 }
1580                 for (int j = 0; j < pendingSetRequests.size(); j++) {
1581                     AsyncPropRequestInfo pendingRequest = pendingSetRequests.get(j);
1582                     if (pendingRequest.getAreaId() != vehiclePropError.areaId) {
1583                         continue;
1584                     }
1585                     removePendingAsyncPropRequestInfoLocked(pendingRequest);
1586                     CarPropertyErrorCodes carPropertyErrorCodes =
1587                             convertVhalStatusCodeToCarPropertyManagerErrorCodes(
1588                                     vehiclePropError.errorCode);
1589                     GetSetValueResult errorResult = pendingRequest.toErrorResult(
1590                             carPropertyErrorCodes);
1591                     Slogf.w(TAG, "Pending async set request received property set error with "
1592                             + "error: %d, vendor error code: %d, fail the pending request: %s",
1593                             carPropertyErrorCodes.getCarPropertyManagerErrorCode(),
1594                             carPropertyErrorCodes.getVendorErrorCode(),
1595                             pendingRequest);
1596                     storeResultForRequest(errorResult, pendingRequest, callbackToSetValueResults);
1597                 }
1598             }
1599             updateSubscriptionRateForAsyncSetRequestLocked();
1600         }
1601         for (VehicleStubCallback callback : callbackToSetValueResults.keySet()) {
1602             callback.sendSetValueResults(callbackToSetValueResults.get(callback));
1603         }
1604     }
1605 
1606     @Override
1607     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(PrintWriter writer)1608     public void dump(PrintWriter writer) {
1609         writer.println(TAG);
1610         writer.println("  Properties available:");
1611         synchronized (mLock) {
1612             for (int i = 0; i < mHalPropIdToPropConfig.size(); i++) {
1613                 HalPropConfig halPropConfig = mHalPropIdToPropConfig.valueAt(i);
1614                 writer.println("    " + halPropConfig);
1615             }
1616             mSubManager.dump(new IndentingPrintWriter(writer));
1617         }
1618     }
1619 
prepareVehicleStubRequests(@syncRequestType int requestType, List<AsyncPropertyServiceRequest> serviceRequests, long timeoutInMs, VehicleStubCallback vehicleStubCallback, @Nullable List<AsyncPropRequestInfo> assocSetValueRequestInfo, @Nullable List<AsyncPropRequestInfo> outRequestInfo, long asyncRequestStartTime)1620     private List<AsyncGetSetRequest> prepareVehicleStubRequests(@AsyncRequestType int requestType,
1621             List<AsyncPropertyServiceRequest> serviceRequests,
1622             long timeoutInMs,
1623             VehicleStubCallback vehicleStubCallback,
1624             @Nullable List<AsyncPropRequestInfo> assocSetValueRequestInfo,
1625             @Nullable List<AsyncPropRequestInfo> outRequestInfo, long asyncRequestStartTime) {
1626         // TODO(b/242326085): Change local variables into memory pool to reduce memory
1627         //  allocation/release cycle
1628         List<AsyncGetSetRequest> vehicleStubRequests = new ArrayList<>();
1629         List<AsyncPropRequestInfo> pendingRequestInfo = new ArrayList<>();
1630         List<GetSetValueResultWrapper> staticGetValueResults = new ArrayList<>();
1631         long nowUptimeMs = SystemClock.uptimeMillis();
1632         synchronized (mLock) {
1633             for (int i = 0; i < serviceRequests.size(); i++) {
1634                 AsyncPropertyServiceRequest serviceRequest = serviceRequests.get(i);
1635                 int propertyId = serviceRequest.getPropertyId();
1636                 int areaId = serviceRequest.getAreaId();
1637                 if (requestType == GET
1638                         && mStaticPropertyIdAreaIdCache.get(propertyId, areaId) != null) {
1639                     if (DBG) {
1640                         Slogf.d(TAG, "Get Async property: %s retrieved from cache",
1641                                 VehiclePropertyIds.toString(propertyId));
1642                     }
1643                     staticGetValueResults.add(new GetSetValueResultWrapper(newGetValueResult(
1644                             serviceRequest.getRequestId(), mStaticPropertyIdAreaIdCache.get(
1645                                     propertyId, areaId)), asyncRequestStartTime,
1646                             /* retryCount= */ 0));
1647                     continue;
1648                 }
1649                 AsyncPropRequestInfo pendingRequest = new AsyncPropRequestInfo(requestType,
1650                         serviceRequest, nowUptimeMs + timeoutInMs, vehicleStubCallback,
1651                         asyncRequestStartTime);
1652                 if (assocSetValueRequestInfo != null) {
1653                     // Link the async set value request and the get init value request together.
1654                     pendingRequest.setAssocSetValueRequestInfo(assocSetValueRequestInfo.get(i));
1655                     assocSetValueRequestInfo.get(i).setAssocGetInitValueRequestInfo(pendingRequest);
1656                 }
1657                 AsyncGetSetRequest vehicleStubRequest = generateVehicleStubAsyncRequestLocked(
1658                         pendingRequest);
1659                 vehicleStubRequests.add(vehicleStubRequest);
1660                 pendingRequestInfo.add(pendingRequest);
1661                 if (outRequestInfo != null) {
1662                     outRequestInfo.add(pendingRequest);
1663                 }
1664             }
1665             mPendingAsyncRequests.addPendingRequests(pendingRequestInfo);
1666         }
1667         if (!staticGetValueResults.isEmpty()) {
1668             vehicleStubCallback.sendGetValueResults(staticGetValueResults);
1669         }
1670         return vehicleStubRequests;
1671     }
1672 
createVehicleStubCallback( IAsyncPropertyResultCallback asyncPropertyResultCallback)1673     VehicleStubCallback createVehicleStubCallback(
1674             IAsyncPropertyResultCallback asyncPropertyResultCallback) {
1675         IBinder asyncPropertyResultBinder = asyncPropertyResultCallback.asBinder();
1676         VehicleStubCallback callback;
1677         synchronized (mLock) {
1678             if (mResultBinderToVehicleStubCallback.get(asyncPropertyResultBinder) == null) {
1679                 callback = new VehicleStubCallback(asyncPropertyResultCallback);
1680                 try {
1681                     callback.linkToDeath(() -> onBinderDied(asyncPropertyResultBinder));
1682                 } catch (RemoteException e) {
1683                     throw new IllegalStateException("Linking to binder death recipient failed, "
1684                             + "the client might already died", e);
1685                 }
1686                 mResultBinderToVehicleStubCallback.put(asyncPropertyResultBinder, callback);
1687             } else {
1688                 callback = mResultBinderToVehicleStubCallback.get(asyncPropertyResultBinder);
1689             }
1690         }
1691         return callback;
1692     }
1693 
sendVehicleStubRequests(@syncRequestType int requestType, List<AsyncGetSetRequest> vehicleStubRequests, VehicleStubCallback callback)1694     private void sendVehicleStubRequests(@AsyncRequestType int requestType,
1695             List<AsyncGetSetRequest> vehicleStubRequests, VehicleStubCallback callback) {
1696         switch (requestType) {
1697             case GET: // fallthrough
1698             case GET_INITIAL_VALUE_FOR_SET:
1699                 mVehicleHal.getAsync(vehicleStubRequests, callback);
1700                 break;
1701             case SET:
1702                 mVehicleHal.setAsync(vehicleStubRequests, callback);
1703                 break;
1704         }
1705     }
1706 
1707     /**
1708      * Queries CarPropertyValue with list of AsyncPropertyServiceRequest objects.
1709      *
1710      * <p>This method gets the CarPropertyValue using async methods. </p>
1711      */
getCarPropertyValuesAsync( List<AsyncPropertyServiceRequest> serviceRequests, IAsyncPropertyResultCallback asyncPropertyResultCallback, long timeoutInMs, long asyncRequestStartTime)1712     public void getCarPropertyValuesAsync(
1713             List<AsyncPropertyServiceRequest> serviceRequests,
1714             IAsyncPropertyResultCallback asyncPropertyResultCallback,
1715             long timeoutInMs, long asyncRequestStartTime) {
1716         VehicleStubCallback vehicleStubCallback = createVehicleStubCallback(
1717                 asyncPropertyResultCallback);
1718         List<AsyncGetSetRequest> vehicleStubRequests = prepareVehicleStubRequests(
1719                 GET, serviceRequests, timeoutInMs, vehicleStubCallback,
1720                 /* assocSetValueRequestInfo= */ null, /* outRequestInfo= */ null,
1721                 asyncRequestStartTime);
1722         if (vehicleStubRequests.isEmpty()) {
1723             return;
1724         }
1725         sendVehicleStubRequests(GET, vehicleStubRequests, vehicleStubCallback);
1726     }
1727 
filterWaitForUpdateRequests(List<T> requests, Function<T, Boolean> isWaitForPropertyUpdate)1728     private <T> List<T> filterWaitForUpdateRequests(List<T> requests,
1729             Function<T, Boolean> isWaitForPropertyUpdate) {
1730         List<T> waitForUpdateSetRequests = new ArrayList<>();
1731         for (int i = 0; i < requests.size(); i++) {
1732             if (isWaitForPropertyUpdate.apply(requests.get(i))) {
1733                 waitForUpdateSetRequests.add(requests.get(i));
1734             }
1735         }
1736         return waitForUpdateSetRequests;
1737     }
1738 
newCarSubscription(int propertyId, int[] areaIds, float updateRateHz, boolean enableVur)1739     private static CarSubscription newCarSubscription(int propertyId, int[] areaIds,
1740                                                       float updateRateHz, boolean enableVur) {
1741         return newCarSubscription(propertyId, areaIds, updateRateHz, enableVur,
1742                 /*resolution*/ 0.0f);
1743     }
1744 
newCarSubscription(int propertyId, int[] areaIds, float updateRateHz, boolean enableVur, float resolution)1745     private static CarSubscription newCarSubscription(int propertyId, int[] areaIds,
1746             float updateRateHz, boolean enableVur, float resolution) {
1747         CarSubscription option = new CarSubscription();
1748         option.propertyId = propertyId;
1749         option.areaIds = areaIds;
1750         option.updateRateHz = updateRateHz;
1751         option.enableVariableUpdateRate = enableVur;
1752         option.resolution = resolution;
1753         return option;
1754     }
1755 
1756     /**
1757      * For every pending async set request that needs to wait for property update, generates an
1758      * async get initial value request and subscribes to the property update event.
1759      */
sendGetInitialValueAndSubscribeUpdateEvent( List<AsyncPropertyServiceRequest> serviceRequests, VehicleStubCallback vehicleStubCallback, long timeoutInMs, List<AsyncPropRequestInfo> waitForUpdateSetRequestInfo, long asyncRequestStartTime)1760     private void sendGetInitialValueAndSubscribeUpdateEvent(
1761             List<AsyncPropertyServiceRequest> serviceRequests,
1762             VehicleStubCallback vehicleStubCallback, long timeoutInMs,
1763             List<AsyncPropRequestInfo> waitForUpdateSetRequestInfo, long asyncRequestStartTime) {
1764         // Stores a list of async GET_INITIAL_VALUE request to be sent.
1765         List<AsyncGetSetRequest> getInitValueRequests = prepareVehicleStubRequests(
1766                 GET_INITIAL_VALUE_FOR_SET, serviceRequests, timeoutInMs,
1767                 vehicleStubCallback, /* assocSetValueRequestInfo= */ waitForUpdateSetRequestInfo,
1768                 /* outRequestInfo= */ null, asyncRequestStartTime);
1769 
1770         // Subscribe to the property's change events before setting the property.
1771         synchronized (mLock) {
1772             for (AsyncPropRequestInfo setRequestInfo : waitForUpdateSetRequestInfo) {
1773                 int halPropId = managerToHalPropId(setRequestInfo.getPropertyId());
1774                 // We already checked in {@code carPropertyValueToHalPropValueLocked} inside
1775                 // {@code prepareVehicleStubRequests}, this is guaranteed not to be null.
1776                 HalPropConfig halPropConfig = mHalPropIdToPropConfig.get(halPropId);
1777 
1778                 setRequestInfo.parseClientUpdateRateHz(halPropConfig.toCarPropertyConfig(
1779                         setRequestInfo.getPropertyId(), mPropertyHalServiceConfigs));
1780 
1781                 if (mHalPropIdToWaitingUpdateRequestInfo.get(halPropId) == null) {
1782                     mHalPropIdToWaitingUpdateRequestInfo.put(halPropId, new ArrayList<>());
1783                 }
1784                 mHalPropIdToWaitingUpdateRequestInfo.get(halPropId).add(setRequestInfo);
1785                 // Internally subscribe to the propId, areaId for property update events.
1786                 // We use the pending async service request ID as client key.
1787                 // Enable VUR for continuous since we only want to know when the value is updated.
1788                 boolean enableVur = (halPropConfig.getChangeMode()
1789                         == CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS);
1790                 mSubManager.stageNewOptions(new ClientType(setRequestInfo.getServiceRequestId()),
1791                         // Note that we use halPropId instead of mgrPropId in mSubManager.
1792                         List.of(newCarSubscription(halPropId,
1793                                 new int[]{setRequestInfo.getAreaId()},
1794                                 setRequestInfo.getUpdateRateHz(), enableVur)));
1795             }
1796             try {
1797                 updateSubscriptionRateLocked();
1798             } catch (ServiceSpecificException e) {
1799                 Slogf.e(TAG, "failed to update subscription rate after we start a new async set "
1800                         + "request, the request will likely time-out", e);
1801             }
1802         }
1803 
1804         sendVehicleStubRequests(GET_INITIAL_VALUE_FOR_SET, getInitValueRequests,
1805                 vehicleStubCallback);
1806     }
1807 
1808     /**
1809      * Sets car property values asynchronously.
1810      */
setCarPropertyValuesAsync( List<AsyncPropertyServiceRequest> serviceRequests, IAsyncPropertyResultCallback asyncPropertyResultCallback, long timeoutInMs, long asyncRequestStartTime)1811     public void setCarPropertyValuesAsync(
1812             List<AsyncPropertyServiceRequest> serviceRequests,
1813             IAsyncPropertyResultCallback asyncPropertyResultCallback,
1814             long timeoutInMs, long asyncRequestStartTime) {
1815         List<AsyncPropRequestInfo> pendingSetRequestInfo = new ArrayList<>();
1816         VehicleStubCallback vehicleStubCallback = createVehicleStubCallback(
1817                 asyncPropertyResultCallback);
1818         List<AsyncGetSetRequest> setValueRequests = prepareVehicleStubRequests(
1819                 SET, serviceRequests, timeoutInMs, vehicleStubCallback,
1820                  /* assocSetValueRequestInfo= */ null, /* outRequestInfo= */ pendingSetRequestInfo,
1821                 asyncRequestStartTime);
1822         List<AsyncPropRequestInfo> waitForUpdateSetRequestInfo = filterWaitForUpdateRequests(
1823                 pendingSetRequestInfo, (request) -> request.isWaitForPropertyUpdate());
1824 
1825         if (waitForUpdateSetRequestInfo.size() != 0) {
1826             List<AsyncPropertyServiceRequest> waitForUpdateServiceRequests =
1827                     filterWaitForUpdateRequests(serviceRequests,
1828                             (request) -> request.isWaitForPropertyUpdate());
1829             sendGetInitialValueAndSubscribeUpdateEvent(waitForUpdateServiceRequests,
1830                     vehicleStubCallback, timeoutInMs, waitForUpdateSetRequestInfo,
1831                     asyncRequestStartTime);
1832         }
1833 
1834         sendVehicleStubRequests(SET, setValueRequests, vehicleStubCallback);
1835     }
1836 
1837     /**
1838      * Maps managerRequestIds to serviceRequestIds and remove them from the pending request map.
1839      */
cancelRequests(int[] managerRequestIds)1840     public void cancelRequests(int[] managerRequestIds) {
1841         List<Integer> serviceRequestIdsToCancel = new ArrayList<>();
1842         Set<Integer> managerRequestIdsSet = CarServiceUtils.toIntArraySet(managerRequestIds);
1843         synchronized (mLock) {
1844             for (int i = 0; i < mPendingAsyncRequests.size(); i++) {
1845                 // For GET_INITIAL_VALUE request, they have the same manager request ID as their
1846                 // associated async set request. While cancelling the async set request, they will
1847                 // be cancelled as well, see {@link cleanupPendingAsyncSetRequestLocked}, so no need
1848                 // to cancel them here.
1849                 AsyncPropRequestInfo propRequestInfo = mPendingAsyncRequests.valueAt(i);
1850                 if (managerRequestIdsSet.contains(propRequestInfo.getManagerRequestId())
1851                         && propRequestInfo.getRequestType() != GET_INITIAL_VALUE_FOR_SET) {
1852                     serviceRequestIdsToCancel.add((int) mPendingAsyncRequests.keyAt(i));
1853                 }
1854             }
1855             cancelRequestsByServiceRequestIdsLocked(serviceRequestIdsToCancel);
1856         }
1857         if (!serviceRequestIdsToCancel.isEmpty()) {
1858             mVehicleHal.cancelRequests(serviceRequestIdsToCancel);
1859         }
1860     }
1861 
onBinderDied(IBinder binder)1862     private void onBinderDied(IBinder binder) {
1863         List<Integer> serviceRequestIdsToCancel = new ArrayList<>();
1864         synchronized (mLock) {
1865             mResultBinderToVehicleStubCallback.remove(binder);
1866             for (int i = 0; i < mPendingAsyncRequests.size(); i++) {
1867                 AsyncPropRequestInfo clientRequestInfo = mPendingAsyncRequests.valueAt(i);
1868                 if (clientRequestInfo.getVehicleStubCallback().getClientBinder() != binder) {
1869                     continue;
1870                 }
1871                 serviceRequestIdsToCancel.add((int) mPendingAsyncRequests.keyAt(i));
1872             }
1873             cancelRequestsByServiceRequestIdsLocked(serviceRequestIdsToCancel);
1874         }
1875         if (!serviceRequestIdsToCancel.isEmpty()) {
1876             mVehicleHal.cancelRequests(serviceRequestIdsToCancel);
1877         }
1878     }
1879 
1880     @GuardedBy("mLock")
cancelRequestsByServiceRequestIdsLocked(List<Integer> serviceRequestIdsToCancel)1881     private void cancelRequestsByServiceRequestIdsLocked(List<Integer> serviceRequestIdsToCancel) {
1882         if (serviceRequestIdsToCancel.isEmpty()) {
1883             return;
1884         }
1885         for (int i = 0; i < serviceRequestIdsToCancel.size(); i++) {
1886             Slogf.w(TAG, "the request for propertyHalService request ID: %d is cancelled",
1887                     serviceRequestIdsToCancel.get(i));
1888             getAndRemovePendingAsyncPropRequestInfoLocked(serviceRequestIdsToCancel.get(i));
1889         }
1890         try {
1891             updateSubscriptionRateLocked();
1892         } catch (ServiceSpecificException e) {
1893             Slogf.e(TAG, " failed to update subscription rate when an async set request is "
1894                     + "cancelled", e);
1895         }
1896     }
1897 
1898     @GuardedBy("mLock")
carPropertyValueToHalPropValueLocked(CarPropertyValue carPropertyValue)1899     private HalPropValue carPropertyValueToHalPropValueLocked(CarPropertyValue carPropertyValue) {
1900         int mgrPropId = carPropertyValue.getPropertyId();
1901         int halPropId = managerToHalPropId(mgrPropId);
1902         HalPropConfig halPropConfig = mHalPropIdToPropConfig.get(halPropId);
1903         if (halPropConfig == null) {
1904             throw new IllegalArgumentException("Property ID: " + mgrPropId + " is not supported");
1905         }
1906         return mPropValueBuilder.build(carPropertyValue, halPropId, halPropConfig);
1907     }
1908 
1909     /**
1910      * Get the pending async requests size.
1911      *
1912      * For test only.
1913      */
countPendingAsyncRequests()1914     public int countPendingAsyncRequests() {
1915         synchronized (mLock) {
1916             return mPendingAsyncRequests.size();
1917         }
1918     }
1919 
1920     /**
1921      * Get the size of the map from hal prop ID to pending async set value requests.
1922      *
1923      * For test only.
1924      */
countHalPropIdToWaitForUpdateRequests()1925     public int countHalPropIdToWaitForUpdateRequests() {
1926         synchronized (mLock) {
1927             return mHalPropIdToWaitingUpdateRequestInfo.size();
1928         }
1929     }
1930 
requestTypeToString(@syncRequestType int requestType)1931     private static String requestTypeToString(@AsyncRequestType int requestType) {
1932         switch (requestType) {
1933             case GET:
1934                 return "GET";
1935             case SET:
1936                 return "SET";
1937             case GET_INITIAL_VALUE_FOR_SET:
1938                 return "GET_INITIAL_VALUE_FOR_SET";
1939             default:
1940                 return "UNKNOWN";
1941         }
1942     }
1943 
1944     @GuardedBy("mLock")
isStaticAndSystemPropertyLocked(int propertyId)1945     private boolean isStaticAndSystemPropertyLocked(int propertyId) {
1946         return mHalPropIdToPropConfig.get(managerToHalPropId(propertyId))
1947                 .getChangeMode() == VEHICLE_PROPERTY_CHANGE_MODE_STATIC
1948                 && isSystemProperty(propertyId);
1949     }
1950 
managerToHalPropId(int mgrPropId)1951     private int managerToHalPropId(int mgrPropId) {
1952         return mPropertyHalServiceConfigs.managerToHalPropId(mgrPropId);
1953     }
1954 
halToManagerPropId(int mgrPropId)1955     private int halToManagerPropId(int mgrPropId) {
1956         return mPropertyHalServiceConfigs.halToManagerPropId(mgrPropId);
1957     }
1958 
halPropIdToName(int halPropId)1959     private String halPropIdToName(int halPropId) {
1960         return mPropertyHalServiceConfigs.halPropIdToName(halPropId);
1961     }
1962 }
1963