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