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