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 17 package android.car.hardware.property; 18 19 import static android.car.feature.Flags.FLAG_CAR_PROPERTY_SUPPORTED_VALUE; 20 21 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 22 import static com.android.car.internal.property.CarPropertyErrorCodes.STATUS_OK; 23 import static com.android.car.internal.property.CarPropertyHelper.SYNC_OP_LIMIT_TRY_AGAIN; 24 import static com.android.car.internal.property.CarPropertyHelper.getPropIdAreaIdsFromCarSubscriptions; 25 import static com.android.car.internal.property.CarPropertyHelper.newPropIdAreaId; 26 import static com.android.car.internal.util.DebugUtils.toAreaIdString; 27 28 import static java.util.Objects.requireNonNull; 29 30 import android.annotation.CallbackExecutor; 31 import android.annotation.FlaggedApi; 32 import android.annotation.FloatRange; 33 import android.annotation.IntDef; 34 import android.annotation.NonNull; 35 import android.annotation.Nullable; 36 import android.annotation.SuppressLint; 37 import android.annotation.SystemApi; 38 import android.car.Car; 39 import android.car.CarManagerBase; 40 import android.car.VehiclePropertyIds; 41 import android.car.builtin.util.Slogf; 42 import android.car.feature.FeatureFlags; 43 import android.car.feature.FeatureFlagsImpl; 44 import android.car.feature.Flags; 45 import android.car.hardware.CarPropertyConfig; 46 import android.car.hardware.CarPropertyValue; 47 import android.os.Binder; 48 import android.os.Build; 49 import android.os.CancellationSignal; 50 import android.os.Handler; 51 import android.os.IBinder; 52 import android.os.ParcelableHolder; 53 import android.os.RemoteException; 54 import android.os.ServiceSpecificException; 55 import android.os.SystemClock; 56 import android.util.ArrayMap; 57 import android.util.ArraySet; 58 import android.util.Log; 59 import android.util.Slog; 60 import android.util.SparseArray; 61 62 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 63 import com.android.car.internal.ICarBase; 64 import com.android.car.internal.SingleMessageHandler; 65 import com.android.car.internal.common.DispatchList; 66 import com.android.car.internal.dep.Trace; 67 import com.android.car.internal.os.HandlerExecutor; 68 import com.android.car.internal.property.AsyncPropertyServiceRequest; 69 import com.android.car.internal.property.AsyncPropertyServiceRequestList; 70 import com.android.car.internal.property.CarPropertyErrorCodes; 71 import com.android.car.internal.property.CarPropertyEventCallbackController; 72 import com.android.car.internal.property.CarPropertyHelper; 73 import com.android.car.internal.property.CarSubscription; 74 import com.android.car.internal.property.GetPropertyConfigListResult; 75 import com.android.car.internal.property.GetSetValueResult; 76 import com.android.car.internal.property.GetSetValueResultList; 77 import com.android.car.internal.property.IAsyncPropertyResultCallback; 78 import com.android.car.internal.property.ISupportedValuesChangeCallback; 79 import com.android.car.internal.property.InputSanitizationUtils; 80 import com.android.car.internal.property.MinMaxSupportedPropertyValue; 81 import com.android.car.internal.property.PropIdAreaId; 82 import com.android.car.internal.property.RawPropertyValue; 83 import com.android.car.internal.property.SubscriptionManager; 84 import com.android.car.internal.util.IntArray; 85 import com.android.car.internal.util.PairSparseArray; 86 import com.android.internal.annotations.GuardedBy; 87 import com.android.internal.annotations.VisibleForTesting; 88 89 import java.lang.annotation.Retention; 90 import java.lang.annotation.RetentionPolicy; 91 import java.lang.ref.WeakReference; 92 import java.util.ArrayList; 93 import java.util.List; 94 import java.util.Map; 95 import java.util.Optional; 96 import java.util.Set; 97 import java.util.concurrent.Executor; 98 import java.util.concurrent.atomic.AtomicInteger; 99 100 /** 101 * Provides an application interface for interacting with the Vehicle specific properties. 102 * For details about the individual properties, see the descriptions in {@link VehiclePropertyIds} 103 */ 104 public class CarPropertyManager extends CarManagerBase { 105 private static final String TAG = "CarPropertyManager"; 106 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 107 private static final int MSG_GENERIC_EVENT = 0; 108 private static final int SYNC_OP_RETRY_SLEEP_IN_MS = 10; 109 private static final int SYNC_OP_RETRY_MAX_COUNT = 10; 110 // The default update rate used when subscribePropertyEvents does not contain updateRateHz as 111 // an argument. 112 private static final float DEFAULT_UPDATE_RATE_HZ = 1f; 113 114 /** 115 * The default timeout in MS for {@link CarPropertyManager#getPropertiesAsync}. 116 */ 117 public static final long ASYNC_GET_DEFAULT_TIMEOUT_MS = 10_000; 118 119 private final SingleMessageHandler<CarPropertyEvent> mHandler; 120 private final ICarProperty mService; 121 private final int mAppTargetSdk; 122 private final Executor mExecutor; 123 private final AtomicInteger mRequestIdCounter = new AtomicInteger(0); 124 @GuardedBy("mLock") 125 private final SparseArray<AsyncPropertyRequestInfo<?, ?>> mRequestIdToAsyncRequestInfo = 126 new SparseArray<>(); 127 private final AsyncPropertyResultCallback mAsyncPropertyResultCallback = 128 new AsyncPropertyResultCallback(); 129 130 private final CarPropertyEventListenerToService mCarPropertyEventToService = 131 new CarPropertyEventListenerToService(this); 132 private final CarServiceSupportedValuesChangeCallback mCarServiceSupportedValuesChangeCallback = 133 new CarServiceSupportedValuesChangeCallback(this); 134 135 // This lock is shared with all CarPropertyEventCallbackController instances to prevent 136 // potential deadlock. 137 private final Object mLock = new Object(); 138 @GuardedBy("mLock") 139 private final Map<CarPropertyEventCallback, CarPropertyEventCallbackController> 140 mCpeCallbackToCpeCallbackController = new ArrayMap<>(); 141 @GuardedBy("mLock") 142 private final SparseArray<ArraySet<CarPropertyEventCallbackController>> 143 mPropIdToCpeCallbackControllerList = new SparseArray<>(); 144 145 private final GetPropertyResultCallback mGetPropertyResultCallback = 146 new GetPropertyResultCallback(); 147 148 private final SetPropertyResultCallback mSetPropertyResultCallback = 149 new SetPropertyResultCallback(); 150 @GuardedBy("mLock") 151 private final SubscriptionManager<CarPropertyEventCallback> mSubscriptionManager = 152 new SubscriptionManager<>(); 153 // Map from [propId, areaId] to set of registered SupportedValuesChangeCallbacks. 154 @GuardedBy("mLock") 155 private final PairSparseArray<ArraySet<SupportedValuesChangeCallback>> 156 mSupportedValuesChangeCallbackByPropIdAreaId = new PairSparseArray<>(); 157 // Map from SupportedValuesChangeCallback to its associated executor. Only one executor is 158 // associated with one callback. 159 @GuardedBy("mLock") 160 private final Map<SupportedValuesChangeCallback, Executor> 161 mExecutorBySupportedValuesChangeCallback = new ArrayMap<>(); 162 // Map from SupportedValuesChangeCallback to its registered set of [propId, areaIds]. This is 163 // the reverse map for mSupportedValuesChangeCallbackByPropIdAreaId. 164 @GuardedBy("mLock") 165 private final Map<SupportedValuesChangeCallback, ArraySet<PropIdAreaId>> 166 mPropIdAreaIdsBySupportedValuesChangeCallback = new ArrayMap<>(); 167 168 private FeatureFlags mFeatureFlags = new FeatureFlagsImpl(); 169 170 /** 171 * Sets fake feature flag for unit testing. 172 * 173 * @hide 174 */ 175 @VisibleForTesting setFeatureFlags(FeatureFlags fakeFeatureFlags)176 public void setFeatureFlags(FeatureFlags fakeFeatureFlags) { 177 mFeatureFlags = fakeFeatureFlags; 178 } 179 180 /** 181 * Application registers {@link CarPropertyEventCallback} object to receive updates and changes 182 * to subscribed Vehicle specific properties. 183 */ 184 public interface CarPropertyEventCallback { 185 /** 186 * Called when a property is updated. 187 * 188 * <p>For an on-change property or a continuous property with Variable Update Rate enabled 189 * (by default), this is called when a property's value or status changes. 190 * 191 * <p>For a continuous property with VUR disabled, this is called periodically based on 192 * the update rate. 193 * 194 * <p>This will also be called once to deliver the initial value (or a value with 195 * unavailable or error status) for every new subscription. 196 * 197 * @param value the new value of the property 198 */ onChangeEvent(CarPropertyValue value)199 void onChangeEvent(CarPropertyValue value); 200 201 /** 202 * Called when an error happens for a recent {@link CarPropertyManager#setProperty}. 203 * 204 * @param propertyId the property ID which has detected an error 205 * @param areaId the area ID which has detected an error 206 * 207 * <p>Client is recommended to override 208 * {@link CarPropertyEventCallback#onErrorEvent(int, int, int)} and override this as no-op. 209 * 210 * <p>For legacy clients, {@link CarPropertyEventCallback#onErrorEvent(int, int, int)} 211 * should use the default implementation, which will internally call this. 212 * 213 * @see CarPropertyEventCallback#onErrorEvent(int, int, int) 214 */ onErrorEvent(int propertyId, int areaId)215 void onErrorEvent(int propertyId, int areaId); 216 217 /** 218 * Called when an error happens for a recent {@link CarPropertyManager#setProperty}. 219 * 220 * <p>Note that {@link CarPropertyManager#setPropertiesAsync} will not trigger this. In the 221 * case of failure, {@link CarPropertyManager.SetPropertyCallback#onFailure} will be called. 222 * 223 * <p>Clients which changed the property value in the areaId most recently will receive 224 * this callback. If multiple clients set a property for the same area ID simultaneously, 225 * which one takes precedence is undefined. Typically, the last set operation 226 * (in the order that they are issued to car's ECU) overrides the previous set operations. 227 * The delivered error reflects the error happened in the last set operation. 228 * 229 * <p>If clients override this, implementation does not have to call 230 * {@link CarPropertyEventCallback#onErrorEvent(int, int)} inside this function. 231 * {@link CarPropertyEventCallback#onErrorEvent(int, int)} should be overridden as no-op. 232 * 233 * @param propertyId the property ID which is detected an error 234 * @param areaId the area ID which is detected an error 235 * @param errorCode the error code is raised in the car 236 */ onErrorEvent(int propertyId, int areaId, @CarSetPropertyErrorCode int errorCode)237 default void onErrorEvent(int propertyId, int areaId, 238 @CarSetPropertyErrorCode int errorCode) { 239 if (DBG) { 240 Slogf.d(TAG, "onErrorEvent: propertyId: %s, areaId: %s, errorCode: %d", 241 VehiclePropertyIds.toString(propertyId), 242 toAreaIdString(propertyId, areaId), errorCode); 243 } 244 onErrorEvent(propertyId, areaId); 245 } 246 } 247 248 /** 249 * A callback {@link CarPropertyManager#getPropertiesAsync} when succeeded or failed. 250 */ 251 public interface GetPropertyCallback { 252 /** 253 * Method called when {@link GetPropertyRequest} successfully gets a result. 254 */ onSuccess(@onNull GetPropertyResult<?> getPropertyResult)255 void onSuccess(@NonNull GetPropertyResult<?> getPropertyResult); 256 257 /** 258 * Method called when {@link GetPropertyRequest} returns an error. 259 */ onFailure(@onNull PropertyAsyncError propertyAsyncError)260 void onFailure(@NonNull PropertyAsyncError propertyAsyncError); 261 } 262 263 /** 264 * A callback {@link CarPropertyManager#setPropertiesAsync} when succeeded or failed. 265 */ 266 public interface SetPropertyCallback { 267 /** 268 * Method called when the {@link SetPropertyRequest} successfully set the value. 269 * 270 * <p>This means: the set value request is successfully sent to vehicle 271 * 272 * <p>and 273 * 274 * <p>either the current property value is already the target value, or we have received a 275 * property update event indicating the value is updated to the target value. 276 * 277 * <p>If multiple clients set a property for the same area ID simultaneously with different 278 * values, the order is undefined. One possible case is that both requests are sent to the 279 * vehicle bus, which causes two property update events. As a result, the success callback 280 * would be called for both clients, but in an undefined order. This means that even if 281 * the success callback is called, it doesn't necessarily mean getting the property would 282 * return the same value you just set. Another client might have changed the value after you 283 * set it. 284 * 285 * <p>If only one requests is successfully processed by the vehicle bus, overwriting the 286 * other request, then only one success callback would be called for one client. The other 287 * client would get the failure callback with 288 * {@link CarPropertyManager#STATUS_ERROR_TIMEOUT} error code. 289 * 290 * <p>If multiple clients set a property for the same area ID simultaneously with the same 291 * value. The success callback for both clients would be called in an undefined order. 292 */ onSuccess(@onNull SetPropertyResult setPropertyResult)293 void onSuccess(@NonNull SetPropertyResult setPropertyResult); 294 295 /** 296 * Method called when {@link SetPropertyRequest} returns an error. 297 */ onFailure(@onNull PropertyAsyncError propertyAsyncError)298 void onFailure(@NonNull PropertyAsyncError propertyAsyncError); 299 } 300 301 /** 302 * An async get/set property request. 303 */ 304 public interface AsyncPropertyRequest { 305 /** 306 * Returns the unique ID for this request. 307 * 308 * <p>Each request must have a unique request ID so the responses can be differentiated. 309 */ getRequestId()310 int getRequestId(); 311 312 /** 313 * Returns the ID for the property of this request. 314 * 315 * <p>The ID must be one of the {@link VehiclePropertyIds} or vendor property IDs. 316 */ getPropertyId()317 int getPropertyId(); 318 319 /** 320 * Returns the area ID for the property of this request. 321 */ getAreaId()322 int getAreaId(); 323 } 324 325 /** 326 * A request for {@link CarPropertyManager#getPropertiesAsync(List, long, CancellationSignal, 327 * Executor, GetPropertyCallback)}. 328 */ 329 public static final class GetPropertyRequest implements AsyncPropertyRequest { 330 private final int mRequestId; 331 private final int mPropertyId; 332 private final int mAreaId; 333 334 /** 335 * @see AsyncPropertyRequest#getRequestId 336 */ 337 @Override getRequestId()338 public int getRequestId() { 339 return mRequestId; 340 } 341 342 /** 343 * @see AsyncPropertyRequest#getPropertyId 344 */ 345 @Override getPropertyId()346 public int getPropertyId() { 347 return mPropertyId; 348 } 349 350 /** 351 * @see AsyncPropertyRequest#getAreaId 352 */ 353 @Override getAreaId()354 public int getAreaId() { 355 return mAreaId; 356 } 357 358 /** 359 * Internal use only. Users should use {@link #generateGetPropertyRequest(int, int)} 360 * instead. 361 */ GetPropertyRequest(int requestId, int propertyId, int areaId)362 private GetPropertyRequest(int requestId, int propertyId, int areaId) { 363 mRequestId = requestId; 364 mPropertyId = propertyId; 365 mAreaId = areaId; 366 } 367 368 /** 369 * Prints out debug message. 370 */ 371 @Override 372 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) toString()373 public String toString() { 374 return new StringBuilder() 375 .append("GetPropertyRequest{request ID: ") 376 .append(mRequestId) 377 .append(", property ID: ") 378 .append(VehiclePropertyIds.toString(mPropertyId)) 379 .append(", area ID: ") 380 .append(toAreaIdString(mPropertyId, mAreaId)) 381 .append("}").toString(); 382 } 383 } 384 385 /** 386 * A request for {@link CarPropertyManager#setPropertiesAsync(List, long, CancellationSignal, 387 * Executor, SetPropertyCallback)}. 388 * 389 * @param <T> the type for the property value, must be one of Object, Boolean, Float, Integer, 390 * Long, Float[], Integer[], Long[], String, byte[], Object[] 391 */ 392 public static final class SetPropertyRequest<T> implements AsyncPropertyRequest { 393 private final int mRequestId; 394 private final int mPropertyId; 395 private final int mAreaId; 396 private float mUpdateRateHz = 0.f; 397 // By default, the async set operation will wait for the property to be updated to the 398 // target value before calling the success callback (or when the target value is the 399 // same as the current value). 400 private boolean mWaitForPropertyUpdate = true; 401 402 /** 403 * The value to set. 404 */ 405 private final T mValue; 406 407 /** 408 * Sets the update rate in Hz for listening for property updates for continuous property. 409 * 410 * <p>If {@code waitForPropertyUpdate} is set to {@code true} (by default) and if the 411 * property is set to a different value than its current value, the success callback will be 412 * called when a property update event for the new value arrived. This option controls how 413 * frequent the property update event should be reported for continuous property. This is 414 * similar to {@code updateRateHz} in {@link CarPropertyManager#registerCallback}. 415 * 416 * <p>This is ignored for non-continuous properties. 417 * 418 * <p>This is ignored if {@code waitForPropertyUpdate} is set to {@code false}. 419 */ setUpdateRateHz(float updateRateHz)420 public void setUpdateRateHz(float updateRateHz) { 421 mUpdateRateHz = updateRateHz; 422 } 423 424 /** 425 * Sets whether to wait for the property update event before calling success callback. 426 * 427 * <p>This arguments controls under what conditions the operation is considered succeeded 428 * and the success callback will be called. 429 * 430 * <p>If this is set to {@code true} (by default), the success callback will be called when 431 * both of the following conditions are met: 432 * 433 * <ul> 434 * <li>the set operation is successfully delivered to vehicle bus. 435 * <li>the {@code mPropertyId}+{@code mAreaId}'s value already equal to {@code mValue} or 436 * is successfully updated to the {@code mValue} through the set operation. 437 * </ul> 438 * 439 * <p>Even if the target value is the same as the current value, we will still send the set 440 * operation to the vehicle bus. If caller wants to reduce unnecessary overhead, caller must 441 * check existing values before issuing the requests. 442 * 443 * <p>If the first condition fails, the error callback will be called. If the second 444 * condition fails, which means we don't see the property updated to the target value within 445 * a specified timeout, the error callback will be called with {@link 446 * #STATUS_ERROR_TIMEOUT}. 447 * 448 * <p>If this is set to {@code false}, the success callback will be called after the 449 * set operation is successfully delivered to vehicle bus. 450 * 451 * <p>Under most cases, client should wait for the property update to verify that the set 452 * operation actually succeeded. 453 * 454 * <p>For cases when the property is write-only (no way to get property update event) or 455 * when the property represents some action, instead of an actual state, e.g. key stroke 456 * where the property's current value is not meaningful, caller should set 457 * {@code waitForPropertyUpdate} to {@code false}. 458 * 459 * <p>For {@code HVAC_TEMPERATURE_VALUE_SUGGESTION}, this must be set to {@code false} 460 * because the updated property value will not be the same as the value to be set. 461 * 462 * <p>Note that even if this is set to {@code true}, it is only guaranteed that the property 463 * value is the target value after the success callback is called if no other clients are 464 * changing the property at the same time. It is always possible that another client changes 465 * the property value after the property is updated to the target value, but before the 466 * client success callback runs. We only guarantee that at some point during the period 467 * after the client issues the request and before the success callback is called, the 468 * property value was set to the target value. 469 */ setWaitForPropertyUpdate(boolean waitForPropertyUpdate)470 public void setWaitForPropertyUpdate(boolean waitForPropertyUpdate) { 471 mWaitForPropertyUpdate = waitForPropertyUpdate; 472 } 473 474 /** 475 * @see AsyncPropertyRequest#getRequestId 476 */ 477 @Override getRequestId()478 public int getRequestId() { 479 return mRequestId; 480 } 481 482 /** 483 * @see AsyncPropertyRequest#getPropertyId 484 */ 485 @Override getPropertyId()486 public int getPropertyId() { 487 return mPropertyId; 488 } 489 490 /** 491 * @see AsyncPropertyRequest#getAreaId 492 */ 493 @Override getAreaId()494 public int getAreaId() { 495 return mAreaId; 496 } 497 498 /** 499 * Gets the property value to set. 500 */ getValue()501 public T getValue() { 502 return mValue; 503 } 504 505 /** 506 * Gets the update rate for listening for property updates. 507 */ getUpdateRateHz()508 public float getUpdateRateHz() { 509 return mUpdateRateHz; 510 } 511 512 /** 513 * Gets whether to wait for property update event before calling success callback. 514 */ isWaitForPropertyUpdate()515 public boolean isWaitForPropertyUpdate() { 516 return mWaitForPropertyUpdate; 517 } 518 519 /** 520 * Internal use only. Users should use {@link #generateSetPropertyRequest(int, int, T)} 521 * instead. 522 */ SetPropertyRequest(int requestId, int propertyId, int areaId, T value)523 private SetPropertyRequest(int requestId, int propertyId, int areaId, T value) { 524 mRequestId = requestId; 525 mPropertyId = propertyId; 526 mAreaId = areaId; 527 mValue = value; 528 } 529 530 /** 531 * Prints out debug message. 532 */ 533 @Override 534 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) toString()535 public String toString() { 536 return new StringBuilder() 537 .append("SetPropertyRequest{request ID: ") 538 .append(mRequestId) 539 .append(", property ID: ") 540 .append(VehiclePropertyIds.toString(mPropertyId)) 541 .append(", area ID: ") 542 .append(toAreaIdString(mPropertyId, mAreaId)) 543 .append(", value: ") 544 .append(mValue) 545 .append(", waitForPropertyUpdate: ") 546 .append(mWaitForPropertyUpdate) 547 .append(", mUpdateRateHz: ") 548 .append(mUpdateRateHz) 549 .append("}").toString(); 550 } 551 } 552 553 /** 554 * An error result for {@link GetPropertyCallback} or {@link SetPropertyCallback}. 555 */ 556 public static final class PropertyAsyncError { 557 private final int mRequestId; 558 private final int mPropertyId; 559 private final int mAreaId; 560 private final CarPropertyErrorCodes mCarPropertyErrorCodes; 561 getRequestId()562 public int getRequestId() { 563 return mRequestId; 564 } 565 getPropertyId()566 public int getPropertyId() { 567 return mPropertyId; 568 } 569 getAreaId()570 public int getAreaId() { 571 return mAreaId; 572 } 573 getErrorCode()574 public @CarPropertyAsyncErrorCode int getErrorCode() { 575 return mCarPropertyErrorCodes.toCarPropertyAsyncErrorCode(); 576 } 577 578 /** 579 * Gets the vendor error codes to allow for more detailed error codes. 580 * 581 * @return the vendor error code if it is set, otherwise 0. A vendor error code will have a 582 * range from 0x0000 to 0xffff. 583 * 584 * @hide 585 */ 586 @SystemApi getVendorErrorCode()587 public int getVendorErrorCode() { 588 return mCarPropertyErrorCodes.getVendorErrorCode(); 589 } 590 591 /** 592 * Gets the detailed system error code. 593 * 594 * These must be a value defined in 595 * {@link DetailedErrorCode}. The values in {@link DetailedErrorCode} 596 * may be extended in the future to include additional error codes. 597 * 598 * @return the detailed error code if it is set, otherwise set to 0. 599 */ 600 @FlaggedApi(Flags.FLAG_CAR_PROPERTY_DETAILED_ERROR_CODES) getDetailedErrorCode()601 public int getDetailedErrorCode() { 602 return mCarPropertyErrorCodes.toDetailedErrorCode(); 603 } 604 605 /** 606 * Creates a new error result for async property request. 607 * 608 * @param requestId the request ID 609 * @param propertyId the property ID in the request 610 * @param areaId the area ID for the property in the request 611 * @param carPropertyErrorCodes the codes indicating the error 612 */ PropertyAsyncError(int requestId, int propertyId, int areaId, CarPropertyErrorCodes carPropertyErrorCodes)613 PropertyAsyncError(int requestId, int propertyId, int areaId, 614 CarPropertyErrorCodes carPropertyErrorCodes) { 615 mRequestId = requestId; 616 mPropertyId = propertyId; 617 mAreaId = areaId; 618 mCarPropertyErrorCodes = carPropertyErrorCodes; 619 } 620 621 /** 622 * Prints out debug message. 623 */ 624 @Override 625 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) toString()626 public String toString() { 627 return new StringBuilder() 628 .append("PropertyAsyncError{request ID: ") 629 .append(mRequestId) 630 .append(", property: ") 631 .append(VehiclePropertyIds.toString(mPropertyId)) 632 .append(", areaId: ") 633 .append(toAreaIdString(mPropertyId, mAreaId)) 634 .append(", error codes: ") 635 .append(mCarPropertyErrorCodes) 636 .append("}").toString(); 637 } 638 } 639 640 /** 641 * A successful result for {@link GetPropertyCallback}. 642 * 643 * @param <T> the type for the property value, must be one of Object, Boolean, Float, Integer, 644 * Long, Float[], Integer[], Long[], String, byte[], Object[] 645 */ 646 public static final class GetPropertyResult<T> { 647 private final int mRequestId; 648 private final int mPropertyId; 649 private final int mAreaId; 650 private final long mTimestampNanos; 651 private final T mValue; 652 653 /** 654 * Returns the unique ID for the {@link GetPropertyRequest} this result is for. 655 */ getRequestId()656 public int getRequestId() { 657 return mRequestId; 658 } 659 660 /** 661 * Returns the property ID for this result. 662 */ getPropertyId()663 public int getPropertyId() { 664 return mPropertyId; 665 } 666 667 /** 668 * Returns the area ID for this result. 669 */ getAreaId()670 public int getAreaId() { 671 return mAreaId; 672 } 673 674 /** 675 * Returns the property's value. 676 */ 677 @NonNull getValue()678 public T getValue() { 679 return mValue; 680 } 681 682 /** 683 * Returns the timestamp in nanoseconds at which the value for the vehicle property 684 * happened. For a given vehicle property, each new timestamp should be monotonically 685 * increasing using the same time base as {@link SystemClock#elapsedRealtimeNanos()}. 686 * 687 * <p>NOTE: Timestamp should be synchronized with other signals from the platform (e.g. 688 * {@link android.location.Location} and {@link android.hardware.SensorEvent} instances). 689 * Ideally, timestamp synchronization error should be below 1 millisecond. 690 */ getTimestampNanos()691 public long getTimestampNanos() { 692 return mTimestampNanos; 693 } 694 695 /** 696 * Creates a new value result for async GetProperty request. 697 * 698 * @param requestId the request ID 699 * @param propertyId the property ID in the request 700 * @param areaId the area ID for the property in the request 701 * @param timestampNanos the timestamp in nanoseconds when this property is updated 702 * @param value the property's value 703 */ GetPropertyResult(int requestId, int propertyId, int areaId, long timestampNanos, @NonNull T value)704 GetPropertyResult(int requestId, int propertyId, int areaId, long timestampNanos, 705 @NonNull T value) { 706 mRequestId = requestId; 707 mPropertyId = propertyId; 708 mAreaId = areaId; 709 mTimestampNanos = timestampNanos; 710 mValue = value; 711 } 712 713 /** 714 * Prints out debug message. 715 */ 716 @Override 717 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) toString()718 public String toString() { 719 return new StringBuilder() 720 .append("GetPropertyResult{type: ") 721 .append(mValue.getClass()) 722 .append(", request ID: ") 723 .append(mRequestId) 724 .append(", property: ") 725 .append(VehiclePropertyIds.toString(mPropertyId)) 726 .append(", areaId: ") 727 .append(toAreaIdString(mPropertyId, mAreaId)) 728 .append(", value: ") 729 .append(mValue) 730 .append(", timestamp: ") 731 .append(mTimestampNanos).append("ns") 732 .append("}").toString(); 733 } 734 } 735 736 /** 737 * A successful result for {@link SetPropertyCallback}. 738 */ 739 public static final class SetPropertyResult { 740 private final int mRequestId; 741 private final int mPropertyId; 742 private final int mAreaId; 743 private final long mUpdateTimestampNanos; 744 745 /** 746 * Gets the ID for the request this result is for. 747 */ getRequestId()748 public int getRequestId() { 749 return mRequestId; 750 } 751 752 /** 753 * Gets the property ID this result is for. 754 */ getPropertyId()755 public int getPropertyId() { 756 return mPropertyId; 757 } 758 759 /** 760 * Gets the area ID this result is for. 761 */ getAreaId()762 public int getAreaId() { 763 return mAreaId; 764 } 765 766 /** 767 * Gets the timestamp in nanoseconds at which the property was updated to the desired value. 768 * 769 * <p>The timestamp will use the same time base as 770 * {@link SystemClock#elapsedRealtimeNanos()}. 771 * 772 * <p>NOTE: If {@code waitForPropertyUpdate} is set to {@code false} for the request, then 773 * this value will be the time when the async set request is successfully sent to the 774 * vehicle bus, not when the property is updated since we have no way of knowing that. 775 * 776 * <p>NOTE: Timestamp should be synchronized with other signals from the platform (e.g. 777 * {@link android.location.Location} and {@link android.hardware.SensorEvent} instances). 778 * Ideally, timestamp synchronization error should be below 1 millisecond. 779 */ getUpdateTimestampNanos()780 public long getUpdateTimestampNanos() { 781 return mUpdateTimestampNanos; 782 } 783 SetPropertyResult(int requestId, int propertyId, int areaId, long updateTimestampNanos)784 SetPropertyResult(int requestId, int propertyId, int areaId, long updateTimestampNanos) { 785 mRequestId = requestId; 786 mPropertyId = propertyId; 787 mAreaId = areaId; 788 mUpdateTimestampNanos = updateTimestampNanos; 789 } 790 791 /** 792 * Prints out debug message. 793 */ 794 @Override 795 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) toString()796 public String toString() { 797 return new StringBuilder() 798 .append("SetPropertyResult{request ID: ") 799 .append(mRequestId) 800 .append(", property: ") 801 .append(VehiclePropertyIds.toString(mPropertyId)) 802 .append(", areaId: ") 803 .append(toAreaIdString(mPropertyId, mAreaId)) 804 .append(", updated timestamp: ") 805 .append(mUpdateTimestampNanos).append("ns") 806 .append("}").toString(); 807 } 808 } 809 810 /** 811 * An abstract interface for converting async get/set result and calling client callbacks. 812 */ 813 private interface PropertyResultCallback<CallbackType, ResultType> { build(int requestId, int propertyId, int areaId, long timestampNanos, @Nullable Object value)814 ResultType build(int requestId, int propertyId, int areaId, long timestampNanos, 815 @Nullable Object value); onSuccess(CallbackType callback, ResultType result)816 void onSuccess(CallbackType callback, ResultType result); onFailure(CallbackType callback, PropertyAsyncError error)817 void onFailure(CallbackType callback, PropertyAsyncError error); 818 } 819 820 /** 821 * Class to hide implementation detail for get/set callbacks. 822 */ 823 private static final class GetPropertyResultCallback implements 824 PropertyResultCallback<GetPropertyCallback, GetPropertyResult> { build(int requestId, int propertyId, int areaId, long timestampNanos, @Nullable Object value)825 public GetPropertyResult build(int requestId, int propertyId, int areaId, 826 long timestampNanos, @Nullable Object value) { 827 return new GetPropertyResult(requestId, propertyId, areaId, timestampNanos, value); 828 } 829 onSuccess(GetPropertyCallback callback, GetPropertyResult result)830 public void onSuccess(GetPropertyCallback callback, GetPropertyResult result) { 831 if (DBG) { 832 Slogf.d(TAG, "delivering success get property result: %s", result); 833 } 834 callback.onSuccess(result); 835 } 836 onFailure(GetPropertyCallback callback, PropertyAsyncError error)837 public void onFailure(GetPropertyCallback callback, PropertyAsyncError error) { 838 if (DBG) { 839 Slogf.d(TAG, "delivering error get property result: %s", error); 840 } 841 callback.onFailure(error); 842 } 843 } 844 845 /** 846 * Class to hide implementation detail for get/set callbacks. 847 */ 848 private static final class SetPropertyResultCallback implements 849 PropertyResultCallback<SetPropertyCallback, SetPropertyResult> { build(int requestId, int propertyId, int areaId, long timestampNanos, @Nullable Object value)850 public SetPropertyResult build(int requestId, int propertyId, int areaId, 851 long timestampNanos, @Nullable Object value) { 852 return new SetPropertyResult(requestId, propertyId, areaId, timestampNanos); 853 } 854 onSuccess(SetPropertyCallback callback, SetPropertyResult result)855 public void onSuccess(SetPropertyCallback callback, SetPropertyResult result) { 856 if (DBG) { 857 Slogf.d(TAG, "delivering success set property result: %s", result); 858 } 859 callback.onSuccess(result); 860 } 861 onFailure(SetPropertyCallback callback, PropertyAsyncError error)862 public void onFailure(SetPropertyCallback callback, PropertyAsyncError error) { 863 if (DBG) { 864 Slogf.d(TAG, "delivering error set property result: %s", error); 865 } 866 callback.onFailure(error); 867 } 868 } 869 870 /** 871 * A class for delivering {@link GetPropertyCallback} or {@link SetPropertyCallback} client 872 * callback when {@code IAsyncPropertyResultCallback} returns results. 873 */ 874 private class AsyncPropertyResultCallback extends IAsyncPropertyResultCallback.Stub { 875 876 @Override asBinder()877 public IBinder asBinder() { 878 return this; 879 } 880 881 @Override onGetValueResults(GetSetValueResultList getValueResults)882 public void onGetValueResults(GetSetValueResultList getValueResults) { 883 this.<GetPropertyRequest, GetPropertyCallback, GetPropertyResult>onResults( 884 getValueResults.getList(), mGetPropertyResultCallback); 885 } 886 887 @Override onSetValueResults(GetSetValueResultList setValueResults)888 public void onSetValueResults(GetSetValueResultList setValueResults) { 889 this.<SetPropertyRequest<?>, SetPropertyCallback, SetPropertyResult>onResults( 890 setValueResults.getList(), mSetPropertyResultCallback); 891 } 892 893 @SuppressLint("WrongConstant") onResults( List<GetSetValueResult> results, PropertyResultCallback<CallbackType, ResultType> propertyResultCallback)894 private <RequestType extends AsyncPropertyRequest, CallbackType, ResultType> void onResults( 895 List<GetSetValueResult> results, 896 PropertyResultCallback<CallbackType, ResultType> propertyResultCallback) { 897 for (int i = 0; i < results.size(); i++) { 898 GetSetValueResult result = results.get(i); 899 int requestId = result.getRequestId(); 900 AsyncPropertyRequestInfo<RequestType, CallbackType> requestInfo; 901 synchronized (mLock) { 902 requestInfo = 903 (AsyncPropertyRequestInfo<RequestType, CallbackType>) 904 mRequestIdToAsyncRequestInfo.get(requestId); 905 mRequestIdToAsyncRequestInfo.remove(requestId); 906 } 907 if (requestInfo == null) { 908 Slogf.w(TAG, "onResults: Request ID: %d" 909 + " might have been completed, cancelled or an exception might have " 910 + "been thrown", requestId); 911 continue; 912 } 913 Executor callbackExecutor = requestInfo.getCallbackExecutor(); 914 CallbackType clientCallback = requestInfo.getCallback(); 915 var errorCodes = result.getCarPropertyErrorCodes(); 916 int propertyId = requestInfo.getRequest().getPropertyId(); 917 String propertyName = VehiclePropertyIds.toString(propertyId); 918 int areaId = requestInfo.getRequest().getAreaId(); 919 if (errorCodes.isOkay()) { 920 CarPropertyValue<?> carPropertyValue = result.getCarPropertyValue(); 921 long timestampNanos; 922 if (carPropertyValue != null) { 923 // This is a get result. 924 int valuePropertyId = carPropertyValue.getPropertyId(); 925 if (propertyId != valuePropertyId) { 926 Slogf.e(TAG, "onResults: Request ID: %d received get " 927 + "property value result, but has mismatch property ID, " 928 + " expect: %s, got: %s", 929 requestId, propertyName, 930 VehiclePropertyIds.toString(valuePropertyId)); 931 } 932 int valueAreaId = carPropertyValue.getAreaId(); 933 if (areaId != valueAreaId) { 934 Slogf.e(TAG, "onResults: Property: %s Request ID: %d " 935 + "received get property value result, but has " 936 + "mismatch area ID, expect: %s, got: %s", 937 propertyName, requestId, 938 toAreaIdString(propertyId, areaId), 939 toAreaIdString(propertyId, valueAreaId)); 940 } 941 timestampNanos = carPropertyValue.getTimestamp(); 942 } else { 943 // This is a set result. 944 timestampNanos = result.getUpdateTimestampNanos(); 945 } 946 947 ResultType clientResult = propertyResultCallback.build( 948 requestId, propertyId, areaId, timestampNanos, 949 carPropertyValue == null ? null : carPropertyValue.getValue()); 950 runOnExecutor(callbackExecutor, () -> propertyResultCallback.onSuccess( 951 clientCallback, clientResult)); 952 } else { 953 runOnExecutor(callbackExecutor, () -> 954 propertyResultCallback.onFailure(clientCallback, 955 new PropertyAsyncError(requestId, propertyId, areaId, 956 errorCodes))); 957 } 958 } 959 } 960 } 961 runOnExecutor(Executor executor, Runnable runnable)962 private static void runOnExecutor(Executor executor, Runnable runnable) { 963 // Must clear binder identity before running client executor. 964 long token = Binder.clearCallingIdentity(); 965 executor.execute(runnable); 966 Binder.restoreCallingIdentity(token); 967 } 968 969 /** 970 * A class to store async get/set property request info. 971 */ 972 private static final class AsyncPropertyRequestInfo<RequestType, CallbackType> { 973 private final RequestType mRequest; 974 private final Executor mCallbackExecutor; 975 private final CallbackType mCallback; 976 getRequest()977 public RequestType getRequest() { 978 return mRequest; 979 } 980 getCallbackExecutor()981 public Executor getCallbackExecutor() { 982 return mCallbackExecutor; 983 } 984 getCallback()985 public CallbackType getCallback() { 986 return mCallback; 987 } 988 AsyncPropertyRequestInfo(RequestType request, Executor callbackExecutor, CallbackType callback)989 private AsyncPropertyRequestInfo(RequestType request, Executor callbackExecutor, 990 CallbackType callback) { 991 mRequest = request; 992 mCallbackExecutor = callbackExecutor; 993 mCallback = callback; 994 } 995 } 996 997 /** Read ONCHANGE sensors. */ 998 public static final float SENSOR_RATE_ONCHANGE = 0f; 999 /** Read sensors at the rate of 1 hertz */ 1000 public static final float SENSOR_RATE_NORMAL = 1f; 1001 /** Read sensors at the rate of 5 hertz */ 1002 public static final float SENSOR_RATE_UI = 5f; 1003 /** Read sensors at the rate of 10 hertz */ 1004 public static final float SENSOR_RATE_FAST = 10f; 1005 /** Read sensors at the rate of 100 hertz */ 1006 public static final float SENSOR_RATE_FASTEST = 100f; 1007 1008 /** 1009 * Status to indicate that set operation failed. Try it again. 1010 */ 1011 public static final int CAR_SET_PROPERTY_ERROR_CODE_TRY_AGAIN = 1; 1012 1013 /** 1014 * Status to indicate that set operation failed because of an invalid argument. 1015 */ 1016 public static final int CAR_SET_PROPERTY_ERROR_CODE_INVALID_ARG = 2; 1017 1018 /** 1019 * Status to indicate that set operation failed because the property is not available. 1020 */ 1021 public static final int CAR_SET_PROPERTY_ERROR_CODE_PROPERTY_NOT_AVAILABLE = 3; 1022 1023 /** 1024 * Status to indicate that set operation failed because car denied access to the property. 1025 */ 1026 public static final int CAR_SET_PROPERTY_ERROR_CODE_ACCESS_DENIED = 4; 1027 1028 /** 1029 * Status to indicate that set operation failed because of a general error in cars. 1030 */ 1031 public static final int CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN = 5; 1032 1033 /** @hide */ 1034 @IntDef(prefix = {"CAR_SET_PROPERTY_ERROR_CODE_"}, value = { 1035 CAR_SET_PROPERTY_ERROR_CODE_TRY_AGAIN, 1036 CAR_SET_PROPERTY_ERROR_CODE_INVALID_ARG, 1037 CAR_SET_PROPERTY_ERROR_CODE_PROPERTY_NOT_AVAILABLE, 1038 CAR_SET_PROPERTY_ERROR_CODE_ACCESS_DENIED, 1039 CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN, 1040 }) 1041 @Retention(RetentionPolicy.SOURCE) 1042 public @interface CarSetPropertyErrorCode {} 1043 1044 /** 1045 * Error indicating that there is an error detected in cars. 1046 */ 1047 public static final int STATUS_ERROR_INTERNAL_ERROR = 1; 1048 /** 1049 * Error indicating that the property is temporarily not available. 1050 */ 1051 public static final int STATUS_ERROR_NOT_AVAILABLE = 2; 1052 /** 1053 * Error indicating the operation has timed-out. 1054 */ 1055 public static final int STATUS_ERROR_TIMEOUT = 3; 1056 1057 /** @hide */ 1058 @IntDef(prefix = {"STATUS_"}, value = { 1059 STATUS_OK, 1060 STATUS_ERROR_INTERNAL_ERROR, 1061 STATUS_ERROR_NOT_AVAILABLE, 1062 STATUS_ERROR_TIMEOUT 1063 }) 1064 @Retention(RetentionPolicy.SOURCE) 1065 public @interface CarPropertyAsyncErrorCode {} 1066 1067 /** 1068 * Get an instance of the CarPropertyManager. 1069 * 1070 * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead. 1071 * 1072 * @param car the Car instance 1073 * @param service the ICarProperty instance 1074 * @hide 1075 */ CarPropertyManager(ICarBase car, @NonNull ICarProperty service)1076 public CarPropertyManager(ICarBase car, @NonNull ICarProperty service) { 1077 super(car); 1078 mService = service; 1079 mAppTargetSdk = getContext().getApplicationInfo().targetSdkVersion; 1080 1081 Handler eventHandler = getEventHandler(); 1082 if (eventHandler == null) { 1083 mHandler = null; 1084 mExecutor = null; 1085 return; 1086 } 1087 mExecutor = new HandlerExecutor(getEventHandler()); 1088 mHandler = new SingleMessageHandler<>(eventHandler.getLooper(), MSG_GENERIC_EVENT) { 1089 @Override 1090 protected void handleEvent(CarPropertyEvent carPropertyEvent) { 1091 handleCarPropertyEvents(List.of(carPropertyEvent)); 1092 } 1093 }; 1094 } 1095 1096 /** 1097 * @deprecated Use 1098 * {@link CarPropertyManager#subscribePropertyEvents(int, float, CarPropertyEventCallback)} 1099 * instead. Note that {@code subscribePropertyEvents} by default has variable update rate on 1100 * for continuous properties, but {@code registerCallback} by default has variable update rate 1101 * off. If you want to keep the current behavior of receiving property events for duplicate 1102 * values (which hurts performance), please specify the variable update rate option via 1103 * {@link Subscription.Builder#setVariableUpdateRateEnabled}. 1104 * 1105 * Registers {@link CarPropertyEventCallback} to get property updates. 1106 * Multiple callbacks can be registered for a single property or the same callback can be used 1107 * for different properties. If the same callback is registered again for the same property, 1108 * it will be updated to new {@code updateRateHz}. 1109 * 1110 * <p>Rate could be one of the following: 1111 * <ul> 1112 * <li>{@link CarPropertyManager#SENSOR_RATE_ONCHANGE}</li> 1113 * <li>{@link CarPropertyManager#SENSOR_RATE_NORMAL}</li> 1114 * <li>{@link CarPropertyManager#SENSOR_RATE_UI}</li> 1115 * <li>{@link CarPropertyManager#SENSOR_RATE_FAST}</li> 1116 * <li>{@link CarPropertyManager#SENSOR_RATE_FASTEST}</li> 1117 * </ul> 1118 * 1119 * <p> 1120 * <b>Note:</b>Rate has no effect if the property has one of the following change modes: 1121 * <ul> 1122 * <li>{@link CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_STATIC}</li> 1123 * <li>{@link CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE}</li> 1124 * </ul> 1125 * 1126 * <p> 1127 * <b>Note:</b> When this function is called, if for the {@code CarPropertyManager} instance, 1128 * this is the first callback registered for the property, it will receive the current 1129 * values for all the areaIds for the property through property change events if they are 1130 * currently okay for reading. If they are not available for reading or in error state, 1131 * property change events with a unavailable or error status will be generated. 1132 * 1133 * <p> 1134 * <b>Note:</b> If the client has {@link android.content.pm.ApplicationInfo#targetSdkVersion} < 1135 * 16, if the client calls this function after the property is already registered 1136 * (aka, this is not the first callback registered for the property), 1137 * it is not guaranteed that the initial current-value event will be generated. If the client 1138 * has {@link android.content.pm.ApplicationInfo#targetSdkVersion} > 16, it is guaranteed that 1139 * the initial current-value event will always be generated for every call. 1140 * 1141 * <p>For properties that might be unavailable for reading because their power state 1142 * is off, property change events containing the property's initial value will be generated 1143 * once their power state is on. 1144 * 1145 * <p>If {@code updateRateHz} is higher than {@link CarPropertyConfig#getMaxSampleRate()}, it 1146 * will be registered with max sample {@code updateRateHz}. 1147 * 1148 * <p>If {@code updateRateHz} is lower than {@link CarPropertyConfig#getMinSampleRate()}, it 1149 * will be registered with min sample {@code updateRateHz}. 1150 * 1151 * <p> 1152 * <b>Note:</b>Caller must check the value of {@link CarPropertyValue#getStatus()} for property 1153 * change events and only use {@link CarPropertyValue#getValue()} when 1154 * {@link CarPropertyValue#getStatus()} is {@link CarPropertyValue#STATUS_AVAILABLE}. If not, 1155 * the {@link CarPropertyValue#getValue()} is meaningless. 1156 * 1157 * <p> 1158 * <b>Note:</b>A property change event may/may not happen when the property's status 1159 * changes. Caller should not depend on the change event to check property's status. For 1160 * properties that might be unavailable because they depend on certain power state, caller 1161 * should subscribe to the power state property (e.g. 1162 * {@link VehiclePropertyIds#HVAC_POWER_ON} for hvac power dependent properties) to decide this 1163 * property's availability. 1164 * 1165 * <p> 1166 * If the registration failed, this will return {@code false}. Caller must check the return 1167 * value to make sure the registration succeeded. 1168 * 1169 * <p> 1170 * If the property is not supported, this will return {@code false}. 1171 * 1172 * <p> 1173 * If the property is supported and the caller does not have read or write permission to it, 1174 * this will return {@code false}. 1175 * 1176 * <p> 1177 * If the caller has write permission but does not have read permission, this will throw 1178 * {@code SecurityException}. 1179 * 1180 * <p>Note that the callback will be executed on the event handler provided to the 1181 * {@link android.car.Car} or the main thread if none was provided. 1182 * 1183 * <p> 1184 * If one {@link CarPropertyEventCallback} is already registered using 1185 * {@link CarPropertyManager#subscribePropertyEvents}, caller must make sure the executor was 1186 * null (using the default executor) when calling subscribePropertyEvents. 1187 * 1188 * @param carPropertyEventCallback the CarPropertyEventCallback to be registered 1189 * @param propertyId the property ID to subscribe 1190 * @param updateRateHz how fast the property events are delivered in Hz 1191 * @return {@code true} if the listener is successfully registered. 1192 * @throws SecurityException if the property is supported and the caller has write permission, 1193 * but does not have read permission. 1194 */ 1195 @Deprecated 1196 @SuppressWarnings("FormatString") registerCallback(@onNull CarPropertyEventCallback carPropertyEventCallback, int propertyId, @FloatRange(from = 0.0, to = 100.0) float updateRateHz)1197 public boolean registerCallback(@NonNull CarPropertyEventCallback carPropertyEventCallback, 1198 int propertyId, @FloatRange(from = 0.0, to = 100.0) float updateRateHz) { 1199 if (DBG) { 1200 Slogf.d(TAG, "registerCallback, callback: %s propertyId: %s, updateRateHz: %f", 1201 carPropertyEventCallback, VehiclePropertyIds.toString(propertyId), 1202 updateRateHz); 1203 } 1204 1205 boolean hasWritePermissionOnly = false; 1206 try { 1207 hasWritePermissionOnly = mService.isSupportedAndHasWritePermissionOnly(propertyId); 1208 } catch (RemoteException e) { 1209 handleRemoteExceptionFromCarService(e); 1210 return false; 1211 } 1212 1213 if (hasWritePermissionOnly) { 1214 throw new SecurityException( 1215 "Only has write permission, missing read permission for property: " 1216 + CarPropertyHelper.propertyIdsToString(List.of(propertyId))); 1217 } 1218 1219 // We require updateRateHz to be within 0 and 100, however, in the previous implementation, 1220 // we did not actually check this range. In order to prevent the existing behavior, and 1221 // to prevent Subscription.Builder.setUpdateRateHz to throw exception, we fit the 1222 // input within the expected range. 1223 if (updateRateHz > 100.0f) { 1224 updateRateHz = 100.0f; 1225 } 1226 if (updateRateHz < 0.0f) { 1227 updateRateHz = 0.0f; 1228 } 1229 CarSubscription subscribeOption = new CarSubscription(); 1230 subscribeOption.propertyId = propertyId; 1231 subscribeOption.updateRateHz = updateRateHz; 1232 // Make sure areaIds is not null. 1233 subscribeOption.areaIds = new int[0]; 1234 try { 1235 return subscribePropertyEventsInternal(List.of(subscribeOption), 1236 /* callbackExecutor= */ null, carPropertyEventCallback); 1237 } catch (IllegalArgumentException | SecurityException e) { 1238 Slogf.w(TAG, "register: PropertyId=%d, exception=%s", propertyId, e); 1239 return false; 1240 } 1241 } 1242 1243 /** 1244 * Subscribes to property events for all areaIds for the property. 1245 * 1246 * <p>For continuous property, variable update rate is enabled. The update rate is 1Hz or 1247 * the max supported rate (if lower than 1hz). 1248 * 1249 * <p>Note that the callback will be executed on the event handler provided to the 1250 * {@link android.car.Car} or the main thread if none was provided. 1251 * 1252 * @param propertyId The ID for the property to subscribe to. 1253 * @param carPropertyEventCallback The callback to deliver property update/error events. 1254 * @return {@code true} if the listener is successfully registered 1255 * @throws SecurityException if the caller does not have read permission to one of the supported 1256 * properties. 1257 * @throws IllegalArgumentException if there are over-lapping areaIds or the executor is 1258 * registered to another callback or one of the properties are 1259 * not supported. 1260 * 1261 * @see #subscribePropertyEvents(int, int, float, boolean, CarPropertyEventCallback) for more 1262 * options. 1263 */ 1264 @FlaggedApi(Flags.FLAG_VARIABLE_UPDATE_RATE) subscribePropertyEvents(int propertyId, @NonNull CarPropertyEventCallback carPropertyEventCallback)1265 public boolean subscribePropertyEvents(int propertyId, 1266 @NonNull CarPropertyEventCallback carPropertyEventCallback) { 1267 return subscribePropertyEvents(List.of( 1268 new Subscription.Builder(propertyId).setUpdateRateHz(DEFAULT_UPDATE_RATE_HZ) 1269 .build()), /* callbackExecutor= */ null, carPropertyEventCallback); 1270 } 1271 1272 /** 1273 * Subscribes to property events for all areaIds for the property. 1274 * 1275 * <p>For continuous property, variable update rate is enabled. 1276 * 1277 * <p>Note that the callback will be executed on the event handler provided to the 1278 * {@link android.car.Car} or the main thread if none was provided. 1279 * 1280 * @param propertyId The ID for the property to subscribe to. 1281 * @param updateRateHz Only meaningful for continuous property. The update rate in Hz. A common 1282 * value is 1Hz. See {@link Subscription.Builder#setUpdateRateHz} for detail. 1283 * @param carPropertyEventCallback The callback to deliver property update/error events. 1284 * @return {@code true} if the listener is successfully registered 1285 * @throws SecurityException if the caller does not have read permission to one of the supported 1286 * properties. 1287 * @throws IllegalArgumentException if there are over-lapping areaIds or the executor is 1288 * registered to another callback or one of the properties are 1289 * not supported. 1290 * 1291 * @see #subscribePropertyEvents(int, int, float, boolean, CarPropertyEventCallback) for more 1292 * options. 1293 */ 1294 @FlaggedApi(Flags.FLAG_VARIABLE_UPDATE_RATE) subscribePropertyEvents(int propertyId, @FloatRange(from = 0.0, to = 100.0) float updateRateHz, @NonNull CarPropertyEventCallback carPropertyEventCallback)1295 public boolean subscribePropertyEvents(int propertyId, 1296 @FloatRange(from = 0.0, to = 100.0) float updateRateHz, 1297 @NonNull CarPropertyEventCallback carPropertyEventCallback) { 1298 return subscribePropertyEvents(List.of( 1299 new Subscription.Builder(propertyId).setUpdateRateHz(updateRateHz).build()), 1300 /* callbackExecutor= */ null, carPropertyEventCallback); 1301 } 1302 1303 1304 /** 1305 * Subscribes to property events for the specific area ID for the property. 1306 * 1307 * <p>For continuous property, variable update rate is enabled. The update rate is 1Hz or 1308 * the max supported rate (if lower than 1hz). 1309 * 1310 * <p>Note that the callback will be executed on the event handler provided to the 1311 * {@link android.car.Car} or the main thread if none was provided. 1312 * 1313 * @param propertyId The ID for the property to subscribe to. 1314 * @param areaId The ID for the area to subscribe to. 1315 * @param carPropertyEventCallback The callback to deliver property update/error events. 1316 * @return {@code true} if the listener is successfully registered 1317 * @throws SecurityException if the caller does not have read permission to one of the supported 1318 * properties. 1319 * @throws IllegalArgumentException if there are over-lapping areaIds or the executor is 1320 * registered to another callback or one of the properties are 1321 * not supported. 1322 * 1323 * @see #subscribePropertyEvents(int, int, float, boolean, CarPropertyEventCallback) for more 1324 * options. 1325 */ 1326 @FlaggedApi(Flags.FLAG_VARIABLE_UPDATE_RATE) subscribePropertyEvents(int propertyId, int areaId, @NonNull CarPropertyEventCallback carPropertyEventCallback)1327 public boolean subscribePropertyEvents(int propertyId, int areaId, 1328 @NonNull CarPropertyEventCallback carPropertyEventCallback) { 1329 return subscribePropertyEvents(List.of( 1330 new Subscription.Builder(propertyId).addAreaId(areaId).setUpdateRateHz(1f) 1331 .build()), 1332 /* callbackExecutor= */ null, carPropertyEventCallback); 1333 } 1334 1335 /** 1336 * Subscribes to property events for the specific area ID for the property. 1337 * 1338 * <p>For continuous property, variable update rate is enabled. 1339 * 1340 * <p>A property event is used to indicate a property's value/status changes (a.k.a 1341 * property update event) or used to indicate a previous {@link #setProperty} operation failed 1342 * (a.k.a property error event). 1343 * 1344 * <p>It is allowed to register multiple {@code carPropertyEventCallback} for a single 1345 * [PropertyId, areaId]. All the registered callbacks will be invoked. 1346 * 1347 * <p>It is only allowed to have one {@code updateRateHz} for a single 1348 * [propertyId, areaId, carPropertyEventCallback] combination. A new {@code updateRateHz} for 1349 * such combination will update the {@code updateRateHz}. 1350 * 1351 * <p>It is only allowed to have one {@code setVariableUpdateRateEnabled} setting for a single 1352 * [propertyId, areaId, carPropertyEventCallback] combination. A new setting will overwrite 1353 * the current setting for the combination. 1354 * 1355 * <p>The {@code carPropertyEventCallback} is executed on a single default event handler thread. 1356 * 1357 * <p> 1358 * <b>Note:</b> When this function is called, if for the {@code CarPropertyManager} instance, 1359 * this is the first callback registered for the [propertyId, areaId], it will receive the 1360 * current values of the subscribed [propertyId, areaId]s through property change events if they 1361 * are currently okay for reading. If they are not available for reading or in error state, 1362 * property change events with a unavailable or error status will be generated. 1363 * 1364 * <p> 1365 * <b>Note:</b> If the client has {@link android.content.pm.ApplicationInfo#targetSdkVersion} < 1366 * 16, if the client calls this function after the [propertyId, areaId] is already registered 1367 * (aka, this is not the first callback registered for the [propertyId, areaId]), 1368 * it is not guaranteed that the initial current-value event will be generated. If the client 1369 * has {@link android.content.pm.ApplicationInfo#targetSdkVersion} > 16, it is guaranteed that 1370 * the initial current-value event will always be generated for every call. 1371 * 1372 * <p>Note that the callback will be executed on the event handler provided to the 1373 * {@link android.car.Car} or the main thread if none was provided. 1374 * 1375 * @param propertyId The ID for the property to subscribe to. 1376 * @param areaId The ID for the area to subscribe to. 1377 * @param updateRateHz Only meaningful for continuous property. The update rate in Hz. A common 1378 * value is 1Hz. See {@link Subscription.Builder#setUpdateRateHz} for detail. 1379 * @param carPropertyEventCallback The callback to deliver property update/error events. 1380 * @return {@code true} if the listener is successfully registered 1381 * @throws SecurityException if the caller does not have read permission to one of the supported 1382 * properties. 1383 * @throws IllegalArgumentException if there are over-lapping areaIds or the executor is 1384 * registered to another callback or one of the properties are 1385 * not supported. 1386 * 1387 * @see #subscribePropertyEvents(List, Executor, CarPropertyEventCallback) for 1388 * more detailed explanation on property subscription and batched subscription usage. 1389 */ 1390 @FlaggedApi(Flags.FLAG_VARIABLE_UPDATE_RATE) subscribePropertyEvents(int propertyId, int areaId, @FloatRange(from = 0.0, to = 100.0) float updateRateHz, @NonNull CarPropertyEventCallback carPropertyEventCallback)1391 public boolean subscribePropertyEvents(int propertyId, int areaId, 1392 @FloatRange(from = 0.0, to = 100.0) float updateRateHz, 1393 @NonNull CarPropertyEventCallback carPropertyEventCallback) { 1394 Subscription subscription = new Subscription.Builder(propertyId).addAreaId(areaId) 1395 .setUpdateRateHz(updateRateHz).build(); 1396 return subscribePropertyEvents(List.of(subscription), /* callbackExecutor= */ null, 1397 carPropertyEventCallback); 1398 } 1399 1400 /** 1401 * Subscribes to multiple [propertyId, areaId]s for property events. 1402 * 1403 * <p> 1404 * If caller don't need use different subscription options among different areaIds for 1405 * one property (e.g. 1 Hz update rate for front-left and 5 Hz update rate for front-right), it 1406 * is recommended to use one {@link Subscription} per property ID. 1407 * 1408 * <p>It is allowed to register multiple {@code carPropertyEventCallback} for a single 1409 * [PropertyId, areaId]. All the registered callbacks will be invoked. 1410 * 1411 * <p>It is only allowed to have one {@code updateRateHz} for a single 1412 * [propertyId, areaId, carPropertyEventCallback] combination. A new {@code updateRateHz} for 1413 * such combination will update the {@code updateRateHz}. 1414 * 1415 * <p>It is only allowed to have one {@code setVariableUpdateRateEnabled} setting for a single 1416 * [propertyId, areaId, carPropertyEventCallback] combination. A new setting will overwrite 1417 * the current setting for the combination. 1418 * 1419 * <p> 1420 * It is allowed to have the same PropertyId in different {@link Subscription}s 1421 * provided in one call. However, they must have non-overlapping AreaIds. A.k.a., one 1422 * [PropertyId, AreaId] must only be associated with one {@link Subscription} within one call. 1423 * Otherwise, {@link IllegalArgumentException} will be thrown. 1424 * 1425 * <p> 1426 * If the 1427 * {@code callbackExecutor} is {@code null}, the callback will be executed on the default event 1428 * handler thread. If no AreaIds are specified, then it will subscribe to all AreaIds for that 1429 * PropertyId. 1430 * 1431 * <p>Only one executor can be registered to a callback. The callback must be unregistered 1432 * before trying to register another executor for the same callback. (E.G. A callback cannot 1433 * have multiple executors) 1434 * 1435 * <p> 1436 * <b>Note:</b>Rate has no effect if the property has one of the following change modes: 1437 * <ul> 1438 * <li>{@link CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_STATIC}</li> 1439 * <li>{@link CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE}</li> 1440 * </ul> 1441 * 1442 * <p> 1443 * If the property has the change mode: 1444 * {@link CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS}, {@code updateRateHz} in 1445 * {@code Subscription} specifies how frequent the property value has to be polled. If 1446 * {@code setVariableUpdateRateEnabled} is not called with {@code false} and variable update 1447 * rate is supported based on 1448 * {@link android.car.hardware.property.AreaIdConfig#isVariableUpdateRateSupported}, 1449 * then the client will receive property update event only when the property's value changes 1450 * (a.k.a behaves the same as {@link CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE}). 1451 * If {@code setVariableUpdateRateEnabled} is called with {@code false} or variable update rate 1452 * is not supported, then the client will receive all the property update events based on the 1453 * update rate even if the events contain the same property value. 1454 * 1455 * <p>See {@link Subscription.Builder#setVariableUpdateRateEnabled} for more detail. 1456 * 1457 * <p> 1458 * <b>Note:</b> When this function is called, the callback will receive the current 1459 * values of the subscribed [propertyId, areaId]s through property change events if they are 1460 * currently okay for reading. If they are not available for reading or in error state, 1461 * property change events with a unavailable or error status will be generated. 1462 * 1463 * <p>For properties that might be unavailable for reading because their power state is off, 1464 * PropertyId change events containing the PropertyId's initial value will be 1465 * generated once their power state is on. 1466 * 1467 * <p>If the update rate specified in the {@code subscriptions} for a given PropertyId is 1468 * higher than the PropertyId's maximum sample rate, the subscription will be registered at the 1469 * PropertyId's maximum sample rate specified by {@link CarPropertyConfig#getMaxSampleRate()}. 1470 1471 * <p>If the update rate specified in the {@code subscriptions} for a given PropertyId is 1472 * lower than the PropertyId's minimum sample rate, the subscription will be registered at the 1473 * PropertyId's minimum sample rate specified by {@link CarPropertyConfig#getMinSampleRate()}. 1474 * 1475 * <p> 1476 * <b>Note:</b>Caller must check the value of {@link CarPropertyValue#getStatus()} for 1477 * PropertyId change events and only use {@link CarPropertyValue#getValue()} when 1478 * {@link CarPropertyValue#getStatus()} is {@link CarPropertyValue#STATUS_AVAILABLE}. If not, 1479 * the {@link CarPropertyValue#getValue()} is meaningless. 1480 * 1481 * <p> 1482 * <b>Note:</b>A PropertyId change event may/may not happen when the PropertyId's status 1483 * changes. Caller should not depend on the change event to check PropertyId's status. For 1484 * properties that might be unavailable because they depend on certain power state, caller 1485 * should subscribe to the power state PropertyId (e.g. {@link VehiclePropertyIds#HVAC_POWER_ON} 1486 * for hvac power dependent properties) to decide this PropertyId's availability. 1487 * 1488 * <p> 1489 * If one {@link CarPropertyEventCallback} is already registered using 1490 * {@link CarPropertyManager#registerCallback}, caller must make sure the executor is 1491 * null (using the default executor) for subscribePropertyEvents. 1492 * 1493 * @param subscriptions A list of subscriptions to add, which specifies PropertyId, AreaId, and 1494 * updateRateHz. Caller should typically use one Subscription for one 1495 * property ID. 1496 * @param callbackExecutor The executor in which the callback is done on. If this is null, the 1497 * callback will be executed on the event handler provided to the 1498 * {@link android.car.Car} or the main thread if none was provided. 1499 * @param carPropertyEventCallback The callback to deliver property update/error events. 1500 * @return {@code true} if the listener is successfully registered 1501 * @throws SecurityException if the caller does not have read permission to one of the supported 1502 * properties. 1503 * @throws IllegalArgumentException if there are over-lapping areaIds or the executor is 1504 * registered to another callback or one of the properties are 1505 * not supported. 1506 */ 1507 @FlaggedApi(Flags.FLAG_BATCHED_SUBSCRIPTIONS) subscribePropertyEvents(@onNull List<Subscription> subscriptions, @Nullable @CallbackExecutor Executor callbackExecutor, @NonNull CarPropertyEventCallback carPropertyEventCallback)1508 public boolean subscribePropertyEvents(@NonNull List<Subscription> subscriptions, 1509 @Nullable @CallbackExecutor Executor callbackExecutor, 1510 @NonNull CarPropertyEventCallback carPropertyEventCallback) { 1511 requireNonNull(subscriptions); 1512 List<CarSubscription> subscribeOptions = convertToCarSubscribeOptions(subscriptions); 1513 return subscribePropertyEventsInternal(subscribeOptions, callbackExecutor, 1514 carPropertyEventCallback); 1515 } 1516 1517 /** 1518 * Converts the {@link Subscription} from client to internal {@link CarSubscription}. 1519 * 1520 * This is only called by APIs with FLAG_BATCHED_SUBSCRIPTIONS. 1521 */ convertToCarSubscribeOptions(List<Subscription> subscriptions)1522 private List<CarSubscription> convertToCarSubscribeOptions(List<Subscription> subscriptions) { 1523 List<CarSubscription> carSubscribeOptions = new ArrayList<>(); 1524 for (int i = 0; i < subscriptions.size(); i++) { 1525 Subscription clientOption = subscriptions.get(i); 1526 CarSubscription internalOption = new CarSubscription(); 1527 internalOption.propertyId = clientOption.getPropertyId(); 1528 internalOption.areaIds = clientOption.getAreaIds(); 1529 internalOption.updateRateHz = clientOption.getUpdateRateHz(); 1530 internalOption.enableVariableUpdateRate = clientOption.isVariableUpdateRateEnabled(); 1531 internalOption.resolution = clientOption.getResolution(); 1532 carSubscribeOptions.add(internalOption); 1533 } 1534 return carSubscribeOptions; 1535 } 1536 subscribePropertyEventsInternal(List<CarSubscription> subscribeOptions, @Nullable @CallbackExecutor Executor callbackExecutor, CarPropertyEventCallback carPropertyEventCallback)1537 private boolean subscribePropertyEventsInternal(List<CarSubscription> subscribeOptions, 1538 @Nullable @CallbackExecutor Executor callbackExecutor, 1539 CarPropertyEventCallback carPropertyEventCallback) { 1540 requireNonNull(carPropertyEventCallback); 1541 validateAreaDisjointness(subscribeOptions); 1542 if (DBG) { 1543 Slogf.d(TAG, "subscribePropertyEvents, callback: %s subscribeOptions: %s", 1544 carPropertyEventCallback, subscribeOptions); 1545 } 1546 int[] noReadPermPropertyIds; 1547 try { 1548 noReadPermPropertyIds = getSupportedNoReadPermPropIds(subscribeOptions); 1549 } catch (RemoteException e) { 1550 handleRemoteExceptionFromCarService(e); 1551 return false; 1552 } 1553 if (noReadPermPropertyIds.length != 0) { 1554 // propertyIdsToString does not work primitive array. 1555 List<Integer> noReadPermPropertyIdsCopy = new ArrayList<>(); 1556 for (int i = 0; i < noReadPermPropertyIds.length; i++) { 1557 noReadPermPropertyIdsCopy.add(noReadPermPropertyIds[i]); 1558 } 1559 throw new SecurityException("Do not have read permissions for properties: " 1560 + CarPropertyHelper.propertyIdsToString(noReadPermPropertyIdsCopy)); 1561 } 1562 1563 if (callbackExecutor == null) { 1564 callbackExecutor = mExecutor; 1565 } 1566 1567 List<CarSubscription> sanitizedSubscribeOptions; 1568 try { 1569 sanitizedSubscribeOptions = sanitizeSubscribeOptions(subscribeOptions); 1570 } catch (IllegalStateException e) { 1571 Slog.e(TAG, "failed to sanitize update rate", e); 1572 return false; 1573 } 1574 List<CarSubscription> updatedSubscribeOptions; 1575 1576 synchronized (mLock) { 1577 CarPropertyEventCallbackController cpeCallbackController = 1578 mCpeCallbackToCpeCallbackController.get(carPropertyEventCallback); 1579 if (cpeCallbackController != null 1580 && cpeCallbackController.getExecutor() != callbackExecutor) { 1581 throw new IllegalArgumentException("A different executor is already associated with" 1582 + " this callback, please use the same executor."); 1583 } 1584 1585 mSubscriptionManager.stageNewOptions(carPropertyEventCallback, 1586 sanitizedSubscribeOptions); 1587 1588 var maybeUpdatedCarSubscriptions = applySubscriptionChangesLocked(); 1589 if (maybeUpdatedCarSubscriptions.isEmpty()) { 1590 Slog.e(TAG, "Subscription failed: failed to apply subscription changes"); 1591 return false; 1592 } 1593 updatedSubscribeOptions = maybeUpdatedCarSubscriptions.get(); 1594 1595 if (cpeCallbackController == null) { 1596 cpeCallbackController = 1597 new CarPropertyEventCallbackController(carPropertyEventCallback, 1598 callbackExecutor); 1599 mCpeCallbackToCpeCallbackController.put(carPropertyEventCallback, 1600 cpeCallbackController); 1601 } 1602 1603 // Must use sanitizedSubscribeOptions instead of subscribeOptions here since we need to 1604 // use sanitized update rate. 1605 for (int i = 0; i < sanitizedSubscribeOptions.size(); i++) { 1606 CarSubscription option = sanitizedSubscribeOptions.get(i); 1607 int propertyId = option.propertyId; 1608 float sanitizedUpdateRateHz = option.updateRateHz; 1609 int[] areaIds = option.areaIds; 1610 // After {@code sanitizeSubscribeOptions}, update rate must be 0 1611 // for on-change property and non-0 for continuous property. 1612 // There is an edge case where minSampleRate is 0 and client uses 0 as sample rate 1613 // for continuous property. In this case, it is really impossible to do VUR so treat 1614 // it as an on-change property is fine. 1615 if (sanitizedUpdateRateHz == 0) { 1616 cpeCallbackController.addOnChangeProperty(propertyId, areaIds); 1617 } else { 1618 cpeCallbackController.addContinuousProperty(propertyId, areaIds, 1619 sanitizedUpdateRateHz, option.enableVariableUpdateRate, 1620 option.resolution); 1621 } 1622 1623 ArraySet<CarPropertyEventCallbackController> cpeCallbackControllerSet = 1624 mPropIdToCpeCallbackControllerList.get(propertyId); 1625 if (cpeCallbackControllerSet == null) { 1626 cpeCallbackControllerSet = new ArraySet<>(); 1627 mPropIdToCpeCallbackControllerList.put(propertyId, cpeCallbackControllerSet); 1628 } 1629 cpeCallbackControllerSet.add(cpeCallbackController); 1630 } 1631 } 1632 1633 if (!mFeatureFlags.alwaysSendInitialValueEvent() || mAppTargetSdk < 36) { 1634 return true; 1635 } 1636 1637 Set<PropIdAreaId> propIdAreaIdsToSubscribe = getPropIdAreaIdsFromCarSubscriptions( 1638 sanitizedSubscribeOptions); 1639 Set<PropIdAreaId> updatedPropIdAreaIds = getPropIdAreaIdsFromCarSubscriptions( 1640 updatedSubscribeOptions); 1641 1642 List<PropIdAreaId> getInitialValuePropIdAreaIds = new ArrayList<>(); 1643 for (var propIdAreaId : propIdAreaIdsToSubscribe) { 1644 if (updatedPropIdAreaIds.contains(propIdAreaId)) { 1645 // If this [propId, areaId] is updated, then car service will generate an initial 1646 // value event, so we don't have to do anything here. 1647 continue; 1648 } 1649 // Otherwise, the request for this [propId, areaId] will not reach car service, hence 1650 // we have to generate the initial value event. 1651 getInitialValuePropIdAreaIds.add(propIdAreaId); 1652 } 1653 1654 if (getInitialValuePropIdAreaIds.isEmpty()) { 1655 return true; 1656 } 1657 1658 try { 1659 mService.getAndDispatchInitialValue(getInitialValuePropIdAreaIds, 1660 mCarPropertyEventToService); 1661 } catch (Exception e) { 1662 Slogf.w(TAG, e, "getAndDispatchInitialValue failed for PropIdAreaIds: %s", 1663 getInitialValuePropIdAreaIds); 1664 } 1665 1666 return true; 1667 } 1668 1669 /** 1670 * Checks if any subscription have overlapping [propertyId, areaId] pairs. 1671 * 1672 * @param subscribeOptions The list of subscribe options to check. 1673 */ validateAreaDisjointness(List<CarSubscription> subscribeOptions)1674 private void validateAreaDisjointness(List<CarSubscription> subscribeOptions) { 1675 PairSparseArray<Object> propertyToAreaId = new PairSparseArray<>(); 1676 Object placeHolder = new Object(); 1677 for (int i = 0; i < subscribeOptions.size(); i++) { 1678 CarSubscription option = subscribeOptions.get(i); 1679 int propertyId = option.propertyId; 1680 int[] areaIds = option.areaIds; 1681 for (int areaId : areaIds) { 1682 if (propertyToAreaId.contains(propertyId, areaId)) { 1683 throw new IllegalArgumentException("Subscribe options contain overlapping " 1684 + "propertyId: " + VehiclePropertyIds.toString(propertyId) + " areaId: " 1685 + toAreaIdString(propertyId, areaId)); 1686 } 1687 propertyToAreaId.append(propertyId, areaId, placeHolder); 1688 } 1689 } 1690 } 1691 1692 private static class CarPropertyEventListenerToService extends ICarPropertyEventListener.Stub { 1693 private final WeakReference<CarPropertyManager> mCarPropertyManager; 1694 CarPropertyEventListenerToService(CarPropertyManager carPropertyManager)1695 CarPropertyEventListenerToService(CarPropertyManager carPropertyManager) { 1696 mCarPropertyManager = new WeakReference<>(carPropertyManager); 1697 } 1698 1699 @Override onEvent(List<CarPropertyEvent> carPropertyEvents)1700 public void onEvent(List<CarPropertyEvent> carPropertyEvents) throws RemoteException { 1701 CarPropertyManager carPropertyManager = mCarPropertyManager.get(); 1702 if (carPropertyManager != null) { 1703 carPropertyManager.handleEvents(carPropertyEvents); 1704 } 1705 } 1706 } 1707 1708 private static final class CarServiceSupportedValuesChangeCallback 1709 extends ISupportedValuesChangeCallback.Stub { 1710 private final WeakReference<CarPropertyManager> mCarPropertyManager; 1711 CarServiceSupportedValuesChangeCallback(CarPropertyManager carPropertyManager)1712 CarServiceSupportedValuesChangeCallback(CarPropertyManager carPropertyManager) { 1713 mCarPropertyManager = new WeakReference<>(carPropertyManager); 1714 } 1715 1716 @Override onSupportedValuesChange(List<PropIdAreaId> propIdAreaIds)1717 public void onSupportedValuesChange(List<PropIdAreaId> propIdAreaIds) { 1718 CarPropertyManager carPropertyManager = mCarPropertyManager.get(); 1719 if (carPropertyManager != null) { 1720 carPropertyManager.handleSupportedValuesChange(propIdAreaIds); 1721 } 1722 } 1723 } 1724 SupportedValuesChangeClientInfo(Executor executor, SupportedValuesChangeCallback callback, int propId, int areaId)1725 private record SupportedValuesChangeClientInfo(Executor executor, 1726 SupportedValuesChangeCallback callback, int propId, int areaId) {} 1727 handleSupportedValuesChange(List<PropIdAreaId> propIdAreaIds)1728 private void handleSupportedValuesChange(List<PropIdAreaId> propIdAreaIds) { 1729 List<SupportedValuesChangeClientInfo> clientInfo = new ArrayList<>(); 1730 synchronized (mLock) { 1731 for (int i = 0; i < propIdAreaIds.size(); i++) { 1732 var propIdAreaId = propIdAreaIds.get(i); 1733 int propId = propIdAreaId.propId; 1734 int areaId = propIdAreaId.areaId; 1735 var clientCallbacks = mSupportedValuesChangeCallbackByPropIdAreaId.get(propId, 1736 areaId); 1737 if (clientCallbacks == null) { 1738 Slogf.w(TAG, "No client callback registered for property: %s, areaId: %s", 1739 VehiclePropertyIds.toString(propId), toAreaIdString(propId, areaId)); 1740 continue; 1741 } 1742 for (int j = 0; j < clientCallbacks.size(); j++) { 1743 var callback = clientCallbacks.valueAt(j); 1744 var callbackExecutor = mExecutorBySupportedValuesChangeCallback.get(callback); 1745 if (callbackExecutor == null) { 1746 Slog.wtf(TAG, "No executor associated with client callback, " 1747 + "must not happen"); 1748 continue; 1749 } 1750 clientInfo.add(new SupportedValuesChangeClientInfo(callbackExecutor, 1751 callback, propId, areaId)); 1752 } 1753 } 1754 } 1755 1756 // Invoke client callback outside of lock scope. 1757 for (int i = 0; i < clientInfo.size(); i++) { 1758 var info = clientInfo.get(i); 1759 runOnExecutor(info.executor(), () -> info.callback().onSupportedValuesChange( 1760 info.propId(), info.areaId())); 1761 } 1762 } 1763 handleEvents(List<CarPropertyEvent> carPropertyEvents)1764 private void handleEvents(List<CarPropertyEvent> carPropertyEvents) { 1765 if (mHandler == null) { 1766 Slog.wtf(TAG, 1767 "Event handler was not created successfully, ignore all property events"); 1768 return; 1769 } 1770 if (mFeatureFlags.handlePropertyEventsInBinderThread()) { 1771 handleCarPropertyEvents(carPropertyEvents); 1772 } else { 1773 mHandler.sendEvents(carPropertyEvents); 1774 } 1775 } 1776 1777 /** 1778 * Update the property ID and area IDs subscription in {@link #mService}. 1779 * 1780 * @return list of updated {@code CarSubscription} if the property has been successfully 1781 * registered with the service or an empty option if failed to register. 1782 * @throws SecurityException if missing the appropriate property access permission. 1783 */ 1784 @GuardedBy("mLock") applySubscriptionChangesLocked()1785 private Optional<List<CarSubscription>> applySubscriptionChangesLocked() { 1786 List<CarSubscription> updatedCarSubscriptions = new ArrayList<>(); 1787 List<Integer> propertiesToUnsubscribe = new ArrayList<>(); 1788 1789 mSubscriptionManager.diffBetweenCurrentAndStage(updatedCarSubscriptions, 1790 propertiesToUnsubscribe); 1791 1792 if (propertiesToUnsubscribe.isEmpty() && updatedCarSubscriptions.isEmpty()) { 1793 if (DBG) { 1794 Slog.d(TAG, "There is nothing to subscribe or unsubscribe to CarPropertyService"); 1795 } 1796 mSubscriptionManager.commit(); 1797 // Returns an empty updated subscriptions here. We must not return empty option 1798 // here because the operation does not fail. 1799 return Optional.of(updatedCarSubscriptions); 1800 } 1801 1802 if (DBG) { 1803 Slogf.d(TAG, "updatedCarSubscriptions to subscribe is: %s" 1804 + " and the list of properties to unsubscribe is: %s", updatedCarSubscriptions, 1805 CarPropertyHelper.propertyIdsToString(propertiesToUnsubscribe)); 1806 } 1807 1808 try { 1809 if (!updatedCarSubscriptions.isEmpty()) { 1810 if (!registerLocked(updatedCarSubscriptions)) { 1811 Slogf.e(TAG, "failed to register subscriptions: %s", updatedCarSubscriptions); 1812 mSubscriptionManager.dropCommit(); 1813 return Optional.empty(); 1814 } 1815 } 1816 1817 if (!propertiesToUnsubscribe.isEmpty()) { 1818 for (int i = 0; i < propertiesToUnsubscribe.size(); i++) { 1819 if (!unregisterLocked(propertiesToUnsubscribe.get(i))) { 1820 Slogf.w(TAG, "Failed to unsubscribe to: %s", 1821 VehiclePropertyIds.toString(propertiesToUnsubscribe.get(i))); 1822 mSubscriptionManager.dropCommit(); 1823 return Optional.empty(); 1824 } 1825 } 1826 } 1827 } catch (SecurityException e) { 1828 Slog.e(TAG, "Received security exception when updating subscription, drop commit", e); 1829 mSubscriptionManager.dropCommit(); 1830 throw e; 1831 } 1832 1833 mSubscriptionManager.commit(); 1834 return Optional.of(updatedCarSubscriptions); 1835 } 1836 1837 /** 1838 * Called when {@code propertyId} registration needs to be updated. 1839 * 1840 * @return {@code true} if registration was successful, otherwise {@code false}. 1841 * @throws SecurityException if missing the appropriate property access permission. 1842 */ 1843 @GuardedBy("mLock") registerLocked(List<CarSubscription> options)1844 private boolean registerLocked(List<CarSubscription> options) { 1845 try { 1846 mService.registerListener(options, mCarPropertyEventToService); 1847 } catch (RemoteException e) { 1848 handleRemoteExceptionFromCarService(e); 1849 return false; 1850 } catch (SecurityException e) { 1851 throw e; 1852 } catch (Exception e) { 1853 Slogf.w(TAG, "registerLocked with options: %s, unexpected exception=%s", options, e); 1854 return false; 1855 } 1856 return true; 1857 } 1858 1859 /** 1860 * Called when {@code propertyId} needs to be unregistered. 1861 * 1862 * @return {@code true} if unregistering was successful, otherwise {@code false}. 1863 * @throws SecurityException if missing the appropriate property access permission. 1864 */ 1865 @GuardedBy("mLock") unregisterLocked(int propertyId)1866 private boolean unregisterLocked(int propertyId) { 1867 try { 1868 mService.unregisterListener(propertyId, mCarPropertyEventToService); 1869 } catch (RemoteException e) { 1870 handleRemoteExceptionFromCarService(e); 1871 return false; 1872 } catch (SecurityException e) { 1873 throw e; 1874 } catch (Exception e) { 1875 Slogf.w(TAG, "unregisterLocked with property: %s, unexpected exception=%s", 1876 VehiclePropertyIds.toString(propertyId), e); 1877 return false; 1878 } 1879 return true; 1880 } 1881 1882 /** 1883 * Stop getting property updates for the given {@link CarPropertyEventCallback}. If there are 1884 * multiple registrations for this {@link CarPropertyEventCallback}, all listening will be 1885 * stopped. 1886 * 1887 * @param carPropertyEventCallback A previously subscribed callback to unsubscribe. 1888 * @throws SecurityException if the caller does not have read permission to the properties 1889 * registered for this callback. 1890 */ 1891 @FlaggedApi(Flags.FLAG_BATCHED_SUBSCRIPTIONS) unsubscribePropertyEvents( @onNull CarPropertyEventCallback carPropertyEventCallback)1892 public void unsubscribePropertyEvents( 1893 @NonNull CarPropertyEventCallback carPropertyEventCallback) { 1894 requireNonNull(carPropertyEventCallback); 1895 if (DBG) { 1896 Slogf.d(TAG, "unsubscribePropertyEvents, callback: %s", carPropertyEventCallback); 1897 } 1898 int[] propertyIds; 1899 synchronized (mLock) { 1900 CarPropertyEventCallbackController cpeCallbackController = 1901 mCpeCallbackToCpeCallbackController.get(carPropertyEventCallback); 1902 if (cpeCallbackController == null) { 1903 Slog.w(TAG, "unsubscribePropertyEvents: callback was not previously registered."); 1904 return; 1905 } 1906 propertyIds = cpeCallbackController.getSubscribedProperties(); 1907 } 1908 ArrayList<Integer> propertyIdsList = new ArrayList<>(propertyIds.length); 1909 for (int i = 0; i < propertyIds.length; i++) { 1910 propertyIdsList.add(propertyIds[i]); 1911 } 1912 unsubscribePropertyEventsInternal(propertyIdsList, carPropertyEventCallback); 1913 } 1914 1915 /** 1916 * @deprecated Use 1917 * {@link CarPropertyManager#unsubscribePropertyEvents(CarPropertyEventCallback)} instead. 1918 * 1919 * Stop getting property updates for the given {@link CarPropertyEventCallback}. If there are 1920 * multiple registrations for this {@link CarPropertyEventCallback}, all listening will be 1921 * stopped. 1922 * 1923 * @param carPropertyEventCallback A previously subscribed callback to unsubscribe. 1924 * @throws SecurityException if the caller does not have read permission to the properties 1925 * registered for this callback. 1926 */ 1927 @Deprecated unregisterCallback(@onNull CarPropertyEventCallback carPropertyEventCallback)1928 public void unregisterCallback(@NonNull CarPropertyEventCallback carPropertyEventCallback) { 1929 if (DBG) { 1930 Slogf.d(TAG, "unregisterCallback, callback: %s", carPropertyEventCallback); 1931 } 1932 requireNonNull(carPropertyEventCallback); 1933 int[] propertyIds; 1934 synchronized (mLock) { 1935 CarPropertyEventCallbackController cpeCallbackController = 1936 mCpeCallbackToCpeCallbackController.get(carPropertyEventCallback); 1937 if (cpeCallbackController == null) { 1938 Slog.w(TAG, "unregisterCallback: callback was not previously registered."); 1939 return; 1940 } 1941 propertyIds = cpeCallbackController.getSubscribedProperties(); 1942 } 1943 ArrayList<Integer> propertyIdsList = new ArrayList<>(propertyIds.length); 1944 for (int i = 0; i < propertyIds.length; i++) { 1945 propertyIdsList.add(propertyIds[i]); 1946 } 1947 unsubscribePropertyEventsInternal(propertyIdsList, carPropertyEventCallback); 1948 } 1949 1950 /** 1951 * Stop getting update for {@code propertyId} to the given {@link CarPropertyEventCallback}. If 1952 * the same {@link CarPropertyEventCallback} is used for other properties, those subscriptions 1953 * will not be affected. 1954 * 1955 * @param propertyId The property ID to unsubscribe. 1956 * @param carPropertyEventCallback A previously subscribed callback to unsubscribe. 1957 * @throws SecurityException if the caller does not have read permission to the property. 1958 */ 1959 @FlaggedApi(Flags.FLAG_BATCHED_SUBSCRIPTIONS) unsubscribePropertyEvents(int propertyId, @NonNull CarPropertyEventCallback carPropertyEventCallback)1960 public void unsubscribePropertyEvents(int propertyId, 1961 @NonNull CarPropertyEventCallback carPropertyEventCallback) { 1962 requireNonNull(carPropertyEventCallback); 1963 unsubscribePropertyEventsInternal(List.of(propertyId), carPropertyEventCallback); 1964 } 1965 1966 /** 1967 * @deprecated Use 1968 * {@link CarPropertyManager#unsubscribePropertyEvents(int, CarPropertyEventCallback)} instead. 1969 * 1970 * Stop getting update for {@code propertyId} to the given {@link CarPropertyEventCallback}. If 1971 * the same {@link CarPropertyEventCallback} is used for other properties, those subscriptions 1972 * will not be affected. 1973 * 1974 * @param carPropertyEventCallback A previously subscribed callback to unsubscribe. 1975 * @param propertyId The property ID to unsubscribe. 1976 * @throws SecurityException if the caller does not have read permission to the property. 1977 */ 1978 @Deprecated 1979 @SuppressWarnings("FormatString") unregisterCallback(@onNull CarPropertyEventCallback carPropertyEventCallback, int propertyId)1980 public void unregisterCallback(@NonNull CarPropertyEventCallback carPropertyEventCallback, 1981 int propertyId) { 1982 if (DBG) { 1983 Slogf.d(TAG, "unregisterCallback, callback: %s, property Id: %s", 1984 carPropertyEventCallback, VehiclePropertyIds.toString(propertyId)); 1985 } 1986 requireNonNull(carPropertyEventCallback); 1987 unsubscribePropertyEventsInternal(List.of(propertyId), carPropertyEventCallback); 1988 } 1989 unsubscribePropertyEventsInternal( List<Integer> propertyIds, CarPropertyEventCallback carPropertyEventCallback)1990 private void unsubscribePropertyEventsInternal( 1991 List<Integer> propertyIds, CarPropertyEventCallback carPropertyEventCallback) { 1992 synchronized (mLock) { 1993 CarPropertyEventCallbackController cpeCallbackController = 1994 mCpeCallbackToCpeCallbackController.get(carPropertyEventCallback); 1995 if (cpeCallbackController == null) { 1996 return; 1997 } 1998 // Filter out User HAL property IDs so that getPropertyConfigsFromService will not 1999 // throw IllegalArgumentException. 2000 List<Integer> filteredPropertyIds = filterOutUserHalProperty(propertyIds); 2001 CarPropertyConfigs configs = getPropertyConfigsFromService(filteredPropertyIds); 2002 2003 if (configs == null) { 2004 Slog.e(TAG, "failed to get property config list from car service, do nothing"); 2005 return; 2006 } 2007 for (int i = 0; i < filteredPropertyIds.size(); i++) { 2008 int propertyId = filteredPropertyIds.get(i); 2009 if (DBG) { 2010 Slogf.d(TAG, "unsubscribePropertyEvents, callback: %s, property Id: %s", 2011 carPropertyEventCallback, VehiclePropertyIds.toString(propertyId)); 2012 } 2013 2014 if (configs.isNotSupported(propertyId)) { 2015 Slog.e(TAG, "unsubscribePropertyEvents: not supported property: " 2016 + VehiclePropertyIds.toString(propertyId)); 2017 continue; 2018 } 2019 if (configs.missingPermission(propertyId)) { 2020 Slog.e(TAG, "unsubscribePropertyEvents: missing read/write permission for " 2021 + "property: " + VehiclePropertyIds.toString(propertyId)); 2022 continue; 2023 } 2024 ArraySet<CarPropertyEventCallbackController> cpeCallbackControllerSet = 2025 mPropIdToCpeCallbackControllerList.get(propertyId); 2026 2027 if (cpeCallbackControllerSet == null) { 2028 Slog.e(TAG, 2029 "unsubscribePropertyEvents: callback was not previously registered."); 2030 continue; 2031 } else if (!cpeCallbackControllerSet.contains(cpeCallbackController)) { 2032 Slog.e(TAG, 2033 "unsubscribePropertyEvents: callback was not previously registered for" 2034 + " propertyId=" + VehiclePropertyIds.toString(propertyId)); 2035 continue; 2036 } 2037 2038 mSubscriptionManager.stageUnregister(carPropertyEventCallback, 2039 new ArraySet<>(Set.of(propertyId))); 2040 2041 if (applySubscriptionChangesLocked().isEmpty()) { 2042 continue; 2043 } 2044 2045 boolean allPropertiesRemoved = cpeCallbackController.remove(propertyId); 2046 if (allPropertiesRemoved) { 2047 mCpeCallbackToCpeCallbackController.remove(carPropertyEventCallback); 2048 } 2049 2050 cpeCallbackControllerSet.remove(cpeCallbackController); 2051 if (cpeCallbackControllerSet.isEmpty()) { 2052 mPropIdToCpeCallbackControllerList.remove(propertyId); 2053 } 2054 } 2055 } 2056 } 2057 2058 /** 2059 * Returns all the list of properties supported by this car that the application may access. 2060 * 2061 * If the caller does not have read/write access to some of the properties, then they will not 2062 * be returned as part of the list, even if the properties are supported by the vehicle. 2063 * 2064 * @return the property config list. 2065 */ 2066 @NonNull getPropertyList()2067 public List<CarPropertyConfig> getPropertyList() { 2068 if (DBG) { 2069 Slog.d(TAG, "getPropertyList"); 2070 } 2071 List<CarPropertyConfig> configs; 2072 try { 2073 configs = mService.getPropertyList().getConfigs(); 2074 } catch (RemoteException e) { 2075 Slog.e(TAG, "getPropertyList exception ", e); 2076 return handleRemoteExceptionFromCarService(e, new ArrayList<>()); 2077 } 2078 if (DBG) { 2079 Slogf.d(TAG, "getPropertyList returns %d configs", configs.size()); 2080 for (int i = 0; i < configs.size(); i++) { 2081 Slogf.v(TAG, "%d: %s", i, configs.get(i)); 2082 } 2083 } 2084 return configs; 2085 } 2086 2087 /** 2088 * Checks the given property IDs and returns a list of property configs supported by the car. 2089 * 2090 * If some of the properties in the given ID list are not supported or if the caller does not 2091 * own the read/write permission to access them, they will not be returned. 2092 * 2093 * @param propertyIds the list of property IDs 2094 * @return the list of property configs 2095 */ 2096 @NonNull getPropertyList(@onNull ArraySet<Integer> propertyIds)2097 public List<CarPropertyConfig> getPropertyList(@NonNull ArraySet<Integer> propertyIds) { 2098 if (DBG) { 2099 Slogf.d(TAG, "getPropertyList(%s)", CarPropertyHelper.propertyIdsToString(propertyIds)); 2100 } 2101 CarPropertyConfigs configs = getPropertyConfigsFromService(propertyIds); 2102 if (configs == null) { 2103 return new ArrayList<>(); 2104 } 2105 if (configs.getMissingPermissionPropIds().length != 0) { 2106 Slog.w(TAG, "Missing required permissions to access properties: " 2107 + CarPropertyHelper.propertyIdsToString(configs.getMissingPermissionPropIds())); 2108 } 2109 if (configs.getUnsupportedPropIds().length != 0) { 2110 Slog.w(TAG, "The following properties are not supported: " 2111 + CarPropertyHelper.propertyIdsToString(configs.getUnsupportedPropIds())); 2112 } 2113 List<CarPropertyConfig> configList = configs.getConfigs(); 2114 if (DBG) { 2115 Slogf.d(TAG, "getPropertyList(%s) returns %d configs", 2116 CarPropertyHelper.propertyIdsToString(propertyIds), configList.size()); 2117 for (int i = 0; i < configList.size(); i++) { 2118 Slog.v(TAG, i + ": " + configList.get(i)); 2119 } 2120 } 2121 return configList; 2122 } 2123 2124 /** 2125 * Gets {@link CarPropertyConfig} by property ID. 2126 * 2127 * @param propertyId the property ID 2128 * @return the {@link CarPropertyConfig} for the selected property, {@code null} if missing 2129 * the required permission to read/write the property or the property is not supported. 2130 */ 2131 @Nullable getCarPropertyConfig(int propertyId)2132 public CarPropertyConfig<?> getCarPropertyConfig(int propertyId) { 2133 if (DBG) { 2134 Slogf.d(TAG, "getCarPropertyConfig(%s)", VehiclePropertyIds.toString(propertyId)); 2135 } 2136 assertNotUserHalProperty(propertyId); 2137 if (!CarPropertyHelper.isSupported(propertyId)) { 2138 Slogf.w(TAG, "Property: %s is not supported", VehiclePropertyIds.toString(propertyId)); 2139 return null; 2140 } 2141 2142 CarPropertyConfigs configs = getPropertyConfigsFromService( 2143 new ArraySet(Set.of(propertyId))); 2144 if (configs == null) { 2145 return null; 2146 } 2147 2148 if (configs.missingPermission(propertyId)) { 2149 Slog.w(TAG, "Missing required permissions to access property: " 2150 + VehiclePropertyIds.toString(propertyId)); 2151 return null; 2152 } 2153 if (configs.isNotSupported(propertyId)) { 2154 Slog.w(TAG, "The property is not supported: " 2155 + VehiclePropertyIds.toString(propertyId)); 2156 return null; 2157 } 2158 2159 CarPropertyConfig<?> config = configs.getConfigs().get(0); 2160 if (DBG) { 2161 Slogf.d(TAG, "getCarPropertyConfig(%s) returns %s", 2162 VehiclePropertyIds.toString(propertyId), config); 2163 } 2164 return config; 2165 } 2166 2167 /** 2168 * Returns areaId contains the selected area for the property. 2169 * 2170 * @param propertyId the property ID 2171 * @param area the area enum such as Enums in {@link android.car.VehicleAreaSeat} 2172 * @throws IllegalArgumentException if the property is not supported in the vehicle for 2173 * the selected area or the caller does not have read or write permission to the property. 2174 * @return the {@code AreaId} containing the selected area for the property 2175 */ getAreaId(int propertyId, int area)2176 public int getAreaId(int propertyId, int area) { 2177 assertNotUserHalProperty(propertyId); 2178 String propertyIdStr = VehiclePropertyIds.toString(propertyId); 2179 if (DBG) { 2180 Slogf.d(TAG, "getAreaId(propertyId=%s, areaId=%s)", 2181 propertyIdStr, toAreaIdString(propertyId, area)); 2182 } 2183 CarPropertyConfigs configs = getPropertyConfigsFromService( 2184 new ArraySet<>(Set.of(propertyId))); 2185 if (configs == null) { 2186 throw new IllegalArgumentException("Failed to getPropertyConfigList from car service"); 2187 } 2188 if (configs.missingPermission(propertyId)) { 2189 throw new IllegalArgumentException("Missing required permissions to access property: " 2190 + propertyIdStr); 2191 } 2192 if (configs.isNotSupported(propertyId)) { 2193 throw new IllegalArgumentException("The property is not supported: " + propertyIdStr); 2194 } 2195 CarPropertyConfig<?> propConfig = configs.getConfigs().get(0); 2196 // For the global property, areaId is 0 2197 if (propConfig.isGlobalProperty()) { 2198 if (DBG) { 2199 Slog.d(TAG, "getAreaId returns the global area ID (0)"); 2200 } 2201 return 0; 2202 } 2203 for (int areaId : propConfig.getAreaIds()) { 2204 if ((area & areaId) == area) { 2205 if (DBG) { 2206 Slog.d(TAG, "getAreaId returns " + toAreaIdString(propertyId, areaId)); 2207 } 2208 return areaId; 2209 } 2210 } 2211 2212 throw new IllegalArgumentException("The propertyId: " + propertyIdStr 2213 + " is not available at the area: " + toAreaIdString(propertyId, area)); 2214 } 2215 2216 /** 2217 * Return read permission string for given property ID. The format of the return value of this 2218 * function has changed over time and thus should not be relied on. 2219 * 2220 * @param propId the property ID to query 2221 * @return the permission needed to read this property, {@code null} if the property ID is not 2222 * available 2223 * 2224 * @hide 2225 */ 2226 @Nullable getReadPermission(int propId)2227 public String getReadPermission(int propId) { 2228 assertNotUserHalProperty(propId); 2229 try { 2230 String permission = mService.getReadPermission(propId); 2231 if (DBG) { 2232 Slogf.d(TAG, "getReadPermission(propId=%s) returns %s", 2233 VehiclePropertyIds.toString(propId), permission); 2234 } 2235 return permission; 2236 } catch (RemoteException e) { 2237 return handleRemoteExceptionFromCarService(e, ""); 2238 } 2239 } 2240 2241 /** 2242 * Return write permission string for given property ID. The format of the return value of this 2243 * function has changed over time and thus should not be relied on. 2244 * 2245 * @param propId the property ID to query 2246 * @return the permission needed to write this property, {@code null} if the property ID is not 2247 * available. 2248 * 2249 * @hide 2250 */ 2251 @Nullable getWritePermission(int propId)2252 public String getWritePermission(int propId) { 2253 assertNotUserHalProperty(propId); 2254 try { 2255 String permission = mService.getWritePermission(propId); 2256 if (DBG) { 2257 Slogf.d(TAG, "getWritePermission(propId=%s) returns %s", 2258 VehiclePropertyIds.toString(propId), permission); 2259 } 2260 return permission; 2261 } catch (RemoteException e) { 2262 return handleRemoteExceptionFromCarService(e, ""); 2263 } 2264 } 2265 2266 2267 /** 2268 * Checks whether a given property is available or disabled based on the car's current state. 2269 * 2270 * @param propertyId the property ID 2271 * @param areaId the area ID 2272 * @return {@code true} if {@link CarPropertyValue#STATUS_AVAILABLE}, {@code false} otherwise 2273 * (eg {@link CarPropertyValue#STATUS_UNAVAILABLE}) 2274 * @throws SecurityException if the client does not have the required read permission to access 2275 * the [propertyId, areaId]. 2276 */ isPropertyAvailable(int propertyId, int areaId)2277 public boolean isPropertyAvailable(int propertyId, int areaId) { 2278 if (DBG) { 2279 Slogf.d(TAG, "isPropertyAvailable(propertyId=%s, areaId=%s)", 2280 VehiclePropertyIds.toString(propertyId), 2281 toAreaIdString(propertyId, areaId)); 2282 } 2283 assertNotUserHalProperty(propertyId); 2284 if (!CarPropertyHelper.isSupported(propertyId)) { 2285 if (DBG) { 2286 Slogf.d(TAG, "Property: %s is not supported", 2287 VehiclePropertyIds.toString(propertyId)); 2288 } 2289 return false; 2290 } 2291 2292 try { 2293 CarPropertyValue propValue = runSyncOperation(() -> { 2294 if (DBG) { 2295 Slog.d(TAG, "calling getProperty to check property's availability"); 2296 } 2297 return mService.getProperty(propertyId, areaId); 2298 }); 2299 return (propValue != null 2300 && propValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE); 2301 } catch (RemoteException e) { 2302 return handleRemoteExceptionFromCarService(e, false); 2303 } catch (ServiceSpecificException | IllegalArgumentException e) { 2304 Slog.e(TAG, "unable to get property", e); 2305 return false; 2306 } 2307 } 2308 2309 /** 2310 * Returns value of a bool property 2311 * 2312 * <p>This method may take couple seconds to complete, so it needs to be called from a 2313 * non-main thread. 2314 * 2315 * <p>Note: Client MUST NOT use one of the following as propertyId, otherwise the behavior is 2316 * undefined: 2317 * 2318 * <ul> 2319 * <li>{@code INITIAL_USER_INFO} 2320 * <li>{@code SWITCH_USER} 2321 * <li>{@code CREATE_USER} 2322 * <li>{@code REMOVE_USER} 2323 * <li>{@code USER_IDENTIFICATION_ASSOCIATION} 2324 * </ul> 2325 * 2326 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} equal 2327 * or later than {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} will receive the following 2328 * exceptions when request failed. 2329 * <ul> 2330 * <li>{@link CarInternalErrorException} when there is an unexpected error detected in cars 2331 * <li>{@link PropertyAccessDeniedSecurityException} when cars denied the access of the 2332 * property 2333 * <li>{@link PropertyNotAvailableAndRetryException} when the property is temporarily 2334 * not available and likely that retrying will be successful 2335 * <li>{@link PropertyNotAvailableException} when the property is not available and might be 2336 * unavailable for a while. 2337 * <li>{@link IllegalArgumentException} when the [propertyId, areaId] is not supported or 2338 * when the property is of wrong type. 2339 * </ul> 2340 * 2341 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} equal 2342 * or later than {@link Build.VERSION_CODES#R}, before 2343 * {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} will 2344 * receive the following exceptions or {@code false} when request failed. 2345 * <ul> 2346 * <li>{@link CarInternalErrorException} when there is an unexpected error detected in cars 2347 * <li>{@link PropertyAccessDeniedSecurityException} when cars denied the access of the 2348 * property 2349 * <li>{@link PropertyNotAvailableAndRetryException} when the property is temporarily 2350 * not available and likely that retrying will be successful 2351 * <li>{@link PropertyNotAvailableException} when the property is not available and might be 2352 * unavailable for a while. 2353 * <li>{@link IllegalArgumentException} when the property is of wrong type. 2354 * <li>{@code false} when the [propertyId, areaId] is not supported 2355 * </ul> 2356 * 2357 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} 2358 * earlier than {@link Build.VERSION_CODES#R} will receive the following exceptions or 2359 * {@code false} when request failed. 2360 * <ul> 2361 * <li>{@link IllegalStateException} when there is an error detected in cars, or when 2362 * cars denied the access of the property, or when the property is not available and 2363 * might be unavailable for a while, or when unexpected error happens. 2364 * <li>{@link IllegalArgumentException} when the property is of wrong type. 2365 * <li>{@code false} when the [propertyId, areaId] is not supported or when the property is 2366 * temporarily not available. 2367 * </ul> 2368 * 2369 * <p>For pre-R client, if the property is temporarily not available, this will return 2370 * {@code false}. 2371 * 2372 * <p>For pre-U client, when the [propertyId, areaId] is not supported, this will return 2373 * {@code false}. 2374 * 2375 * <p>For U and later client, when the [propertyId, areaId] is not supported, this is 2376 * guaranteed to throw {@code IllegalArgumentException}. 2377 * 2378 * @param propertyId the property ID to get 2379 * @param areaId the area ID of the property to get 2380 * 2381 * @throws IllegalStateException when there is an error detected in cars, or when cars denied 2382 * the access of the [propertyId, areaId], or when the [propertyId, areaId] is not available and 2383 * might be unavailable for a while, or when unexpected error happens for pre-R clients. 2384 * @throws CarInternalErrorException when there is an unexpected error detected in cars for 2385 * R and later clients. 2386 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the 2387 * property for R and later clients. 2388 * @throws PropertyNotAvailableAndRetryException when [propertyId, areaId] is temporarily 2389 * not available and likely that retrying will be successful for R and later clients. 2390 * @throws PropertyNotAvailableException when [propertyId, areaId] is not available and might be 2391 * unavailable for a while for R and later clients. 2392 * @throws IllegalArgumentException when the [propertyId, areaId] is not supported for U and 2393 * later client, or when the specified class does not match the property type. 2394 * @throws SecurityException when the client does not have the required read permission to 2395 * access the [propertyId, areaId]. 2396 * 2397 * @return the value of a bool property or {@code false}. 2398 */ getBooleanProperty(int propertyId, int areaId)2399 public boolean getBooleanProperty(int propertyId, int areaId) { 2400 CarPropertyValue<Boolean> carProp = getProperty(Boolean.class, propertyId, areaId); 2401 return handleNullAndPropertyStatus(carProp, areaId, false); 2402 } 2403 2404 /** 2405 * Returns value of a float property 2406 * 2407 * <p>This method may take couple seconds to complete, so it needs to be called from a 2408 * non-main thread. 2409 * 2410 * <p>This method has the same exception behavior as {@link #getBooleanProperty(int, int)}. 2411 * 2412 * <p>For pre-R client, if the property is temporarily not available, this will return 2413 * {@code 0}. 2414 * 2415 * <p>For pre-U client, when the [propertyId, areaId] is not supported, this will return 2416 * {@code 0}. 2417 * 2418 * @param propertyId the property ID to get 2419 * @param areaId the area ID of the property to get 2420 * 2421 * @throws IllegalStateException when there is an error detected in cars, or when cars denied 2422 * the access of the [propertyId, areaId], or when the [propertyId, areaId] is not available and 2423 * might be unavailable for a while, or when unexpected error happens for pre-R clients. 2424 * @throws CarInternalErrorException when there is an unexpected error detected in cars for 2425 * R and later clients. 2426 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the 2427 * property for R and later clients. 2428 * @throws PropertyNotAvailableAndRetryException when [propertyId, areaId] is temporarily 2429 * not available and likely that retrying will be successful for R and later clients. 2430 * @throws PropertyNotAvailableException when [propertyId, areaId] is not available and might be 2431 * unavailable for a while for R and later clients. 2432 * @throws IllegalArgumentException when the [propertyId, areaId] is not supported for U and 2433 * later client, or when the specified class does not match the property type. 2434 * @throws SecurityException when the client does not have the required read permission to 2435 * access the [propertyId, areaId]. 2436 * 2437 * @return the value of a float property or {@code 0}. 2438 */ getFloatProperty(int propertyId, int areaId)2439 public float getFloatProperty(int propertyId, int areaId) { 2440 CarPropertyValue<Float> carProp = getProperty(Float.class, propertyId, areaId); 2441 return handleNullAndPropertyStatus(carProp, areaId, 0f); 2442 } 2443 2444 /** 2445 * Returns value of an integer property 2446 * 2447 * <p>This method may take couple seconds to complete, so it needs to be called form a 2448 * non-main thread. 2449 * 2450 * <p>This method has the same exception behavior as {@link #getBooleanProperty(int, int)}. 2451 * 2452 * <p>For pre-R client, if the property is temporarily not available, this will return 2453 * {@code 0}. 2454 * 2455 * <p>For pre-U client, when the [propertyId, areaId] is not supported, this will return 2456 * {@code 0}. 2457 * 2458 * @param propertyId the property ID to get 2459 * @param areaId the area ID of the property to get 2460 * 2461 * @throws IllegalStateException when there is an error detected in cars, or when cars denied 2462 * the access of the [propertyId, areaId], or when the [propertyId, areaId] is not available and 2463 * might be unavailable for a while, or when unexpected error happens for pre-R clients. 2464 * @throws CarInternalErrorException when there is an unexpected error detected in cars for 2465 * R and later clients. 2466 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the 2467 * property for R and later clients. 2468 * @throws PropertyNotAvailableAndRetryException when [propertyId, areaId] is temporarily 2469 * not available and likely that retrying will be successful for R and later clients. 2470 * @throws PropertyNotAvailableException when [propertyId, areaId] is not available and might be 2471 * unavailable for a while for R and later clients. 2472 * @throws IllegalArgumentException when the [propertyId, areaId] is not supported for U and 2473 * later client, or when the specified class does not match the property type. 2474 * @throws SecurityException when the client does not have the required read permission to 2475 * access the [propertyId, areaId]. 2476 * 2477 * @return the value of aa integer property or {@code 0}. 2478 */ getIntProperty(int propertyId, int areaId)2479 public int getIntProperty(int propertyId, int areaId) { 2480 CarPropertyValue<Integer> carProp = getProperty(Integer.class, propertyId, areaId); 2481 return handleNullAndPropertyStatus(carProp, areaId, 0); 2482 } 2483 2484 /** 2485 * Returns value of an integer array property 2486 * 2487 * <p>This method may take couple seconds to complete, so it needs to be called from a 2488 * non-main thread. 2489 * 2490 * <p>This method has the same exception behavior as {@link #getBooleanProperty(int, int)}. 2491 * 2492 * <p>For pre-R client, if the property is temporarily not available, this will return 2493 * an empty array. 2494 * 2495 * <p>For pre-U client, when the [propertyId, areaId] is not supported, this will return 2496 * an empty array. 2497 * 2498 * @param propertyId the property ID to get 2499 * @param areaId the area ID of the property to get 2500 * 2501 * @throws CarInternalErrorException when there is an unexpected error detected in cars 2502 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the 2503 * property 2504 * @throws PropertyNotAvailableAndRetryException when [propertyId, areaId] is temporarily 2505 * not available and likely that retrying will be successful 2506 * @throws PropertyNotAvailableException when [propertyId, areaId] is not available and might be 2507 * unavailable for a while. 2508 * @throws IllegalArgumentException when the [propertyId, areaId] is not supported for U and 2509 * later client, or when the property is of wrong type. 2510 * 2511 * @return the value of an integer array property or an empty array. 2512 */ 2513 @NonNull getIntArrayProperty(int propertyId, int areaId)2514 public int[] getIntArrayProperty(int propertyId, int areaId) { 2515 CarPropertyValue<Integer[]> carProp = getProperty(Integer[].class, propertyId, areaId); 2516 Integer[] res = handleNullAndPropertyStatus(carProp, areaId, new Integer[0]); 2517 return toIntArray(res); 2518 } 2519 toIntArray(Integer[] input)2520 private static int[] toIntArray(Integer[] input) { 2521 int len = input.length; 2522 int[] arr = new int[len]; 2523 for (int i = 0; i < len; i++) { 2524 arr[i] = input[i]; 2525 } 2526 return arr; 2527 } 2528 handleNullAndPropertyStatus(CarPropertyValue<T> propertyValue, int areaId, T defaultValue)2529 private <T> T handleNullAndPropertyStatus(CarPropertyValue<T> propertyValue, int areaId, 2530 T defaultValue) { 2531 if (propertyValue == null) { 2532 return defaultValue; 2533 } 2534 2535 // Keeps the same behavior as android R. 2536 if (mAppTargetSdk < Build.VERSION_CODES.S) { 2537 return propertyValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE 2538 ? propertyValue.getValue() : defaultValue; 2539 } 2540 2541 // throws new exceptions in android S. 2542 switch (propertyValue.getStatus()) { 2543 case CarPropertyValue.STATUS_ERROR: 2544 throw new CarInternalErrorException(propertyValue.getPropertyId(), areaId); 2545 case CarPropertyValue.STATUS_UNAVAILABLE: 2546 throw new PropertyNotAvailableException(propertyValue.getPropertyId(), 2547 areaId, /*vendorErrorCode=*/0); 2548 default: 2549 return propertyValue.getValue(); 2550 } 2551 } 2552 2553 @FunctionalInterface 2554 private interface RemoteCallable<V> { call()2555 V call() throws RemoteException; 2556 } 2557 2558 @Nullable runSyncOperation(RemoteCallable<V> c)2559 private static <V> V runSyncOperation(RemoteCallable<V> c) 2560 throws RemoteException, ServiceSpecificException { 2561 int retryCount = 0; 2562 while (retryCount < SYNC_OP_RETRY_MAX_COUNT) { 2563 retryCount++; 2564 try { 2565 return c.call(); 2566 } catch (ServiceSpecificException e) { 2567 if (e.errorCode != SYNC_OP_LIMIT_TRY_AGAIN) { 2568 throw e; 2569 } 2570 // If car service don't have enough binder thread to handle this request. Sleep for 2571 // 10ms and try again. 2572 Slogf.d(TAG, "too many sync request, sleeping for %d ms before retry", 2573 SYNC_OP_RETRY_SLEEP_IN_MS); 2574 SystemClock.sleep(SYNC_OP_RETRY_SLEEP_IN_MS); 2575 } catch (RemoteException e) { 2576 throw e; 2577 } 2578 } 2579 throw new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR, 2580 "failed to call car service sync operations after " + retryCount + " retries"); 2581 } 2582 2583 /** 2584 * Return {@link CarPropertyValue} 2585 * 2586 * <p>This method may take couple seconds to complete, so it needs to be called from a 2587 * non-main thread. 2588 * 2589 * <p>Note: Client MUST NOT use one of the following as propertyId, otherwise the behavior is 2590 * undefined (might throw exception or might return null): 2591 * 2592 * <ul> 2593 * <li>{@code INITIAL_USER_INFO} 2594 * <li>{@code SWITCH_USER} 2595 * <li>{@code CREATE_USER} 2596 * <li>{@code REMOVE_USER} 2597 * <li>{@code USER_IDENTIFICATION_ASSOCIATION} 2598 * </ul> 2599 * 2600 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} equal 2601 * or later than {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} will receive the following 2602 * exceptions when request failed. 2603 * <ul> 2604 * <li>{@link CarInternalErrorException} when there is an unexpected error detected in cars 2605 * <li>{@link PropertyAccessDeniedSecurityException} when cars denied the access of the 2606 * [propertyId, areaId]. 2607 * <li>{@link PropertyNotAvailableAndRetryException} when the [propertyId, areaId] is 2608 * temporarily not available and likely that retrying will be successful 2609 * <li>{@link PropertyNotAvailableException} when the [propertyId, areaId] is not available 2610 * and might be unavailable for a while. 2611 * <li>{@link IllegalArgumentException} when the [propertyId, areaId] is not supported or 2612 * when the specified class does not match the property type. 2613 * </ul> 2614 * 2615 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} equal 2616 * or later than {@link Build.VERSION_CODES#R}, before 2617 * {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} will 2618 * receive the following exceptions or {@code null} when request failed. 2619 * <ul> 2620 * <li>{@link CarInternalErrorException} when there is an unexpected error detected in cars 2621 * <li>{@link PropertyAccessDeniedSecurityException} when cars denied the access of the 2622 * [propertyId, areaId]. 2623 * <li>{@link PropertyNotAvailableAndRetryException} when the [propertyId, areaId] is 2624 * temporarily not available and likely that retrying will be successful 2625 * <li>{@link PropertyNotAvailableException} when the [propertyId, areaId] is not available 2626 * and might be unavailable for a while. 2627 * <li>{@link IllegalArgumentException} when the specified class does not match the property 2628 * type. 2629 * <li>{@code null} when the [propertyId, areaId] is not supported 2630 * </ul> 2631 * 2632 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} 2633 * earlier than {@link Build.VERSION_CODES#R} will receive the following exceptions or 2634 * {@code null} when request failed. 2635 * <ul> 2636 * <li>{@link IllegalStateException} when there is an error detected in cars, or when 2637 * cars denied the access of the [propertyId, areaId], or when the [propertyId, areaId] 2638 * is not available and might be unavailable for a while, or when unexpected error 2639 * happens. 2640 * <li>{@link IllegalArgumentException} when the specified class does not match the 2641 * property type. 2642 * <li>{@code null} when the [propertyId, areaId] is not supported or when the property is 2643 * temporarily not available. 2644 * </ul> 2645 * 2646 * <p>For pre-R client, the returned value might be null if the property is temporarily not 2647 * available. The client should try again in this case. 2648 * 2649 * <p>For pre-U client, when the [propertyId, areaId] is not supported, this will return 2650 * {@code null}. 2651 * 2652 * <p>For pre-U client, the returned {@link CarPropertyValue} might contain unavailable or 2653 * error status. Client must use {@link CarPropertyValue#getStatus} to check. If the returned 2654 * status is not {@link CarPropertyValue#STATUS_AVAILABLE}, then the value returned via 2655 * {@link CarPropertyValue#getValue} is undefined. 2656 * 2657 * <p>For U and later client, when the [propertyId, areaId] is not supported, this is 2658 * guaranteed to throw {@code IllegalArgumentException}. This method will never return 2659 * {@code null}. 2660 * 2661 * <p>For U and later client, if the property's status is 2662 * {@link CarPropertyValue#STATUS_UNAVAILABLE}, then {@link PropertyNotAvailableException} will 2663 * be thrown. If the property's status is {@link CarPropertyValue#STATUS_ERROR}, then 2664 * {@link CarInternalErrorException} will be thrown. If no exception is thrown, the returned 2665 * {@link CarPropertyValue#getStatus} is guaranteed to be 2666 * {@link CarPropertyValue#STATUS_AVAILABLE} so client do not need to check. 2667 * 2668 * @param clazz the class object for the CarPropertyValue 2669 * @param propertyId the property ID to get 2670 * @param areaId the area ID of the property to get 2671 * 2672 * @throws IllegalStateException when there is an error detected in cars, or when cars denied 2673 * the access of the [propertyId, areaId], or when the [propertyId, areaId] is not available and 2674 * might be unavailable for a while, or when unexpected error happens for pre-R clients. 2675 * @throws CarInternalErrorException when there is an unexpected error detected in cars for 2676 * R and later clients. 2677 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the 2678 * property for R and later clients. 2679 * @throws PropertyNotAvailableAndRetryException when [propertyId, areaId] is temporarily 2680 * not available and likely that retrying will be successful for R and later clients. 2681 * @throws PropertyNotAvailableException when [propertyId, areaId] is not available and might be 2682 * unavailable for a while for R and later clients. 2683 * @throws IllegalArgumentException when the [propertyId, areaId] is not supported for U and 2684 * later client, or when the specified class does not match the property type. 2685 * @throws SecurityException when the client does not have the required read permission to 2686 * access the [propertyId, areaId]. 2687 * 2688 * @return the value of a property or {@code null}. 2689 */ 2690 @SuppressWarnings("unchecked") 2691 @Nullable getProperty(@onNull Class<E> clazz, int propertyId, int areaId)2692 public <E> CarPropertyValue<E> getProperty(@NonNull Class<E> clazz, int propertyId, 2693 int areaId) { 2694 CarPropertyValue<E> carPropertyValue = getProperty(propertyId, areaId); 2695 if (carPropertyValue == null) { 2696 return null; 2697 } 2698 Class<?> actualClass = carPropertyValue.getValue().getClass(); 2699 if (actualClass != clazz) { 2700 throw new IllegalArgumentException( 2701 "Invalid property type. " + "Expected: " + clazz + ", but was: " 2702 + actualClass); 2703 } 2704 return carPropertyValue; 2705 } 2706 2707 /** 2708 * Query {@link CarPropertyValue} with property id and areaId. 2709 * 2710 * <p>This method may take couple seconds to complete, so it needs to be called from a 2711 * non-main thread. 2712 * 2713 * <p>Note: Client MUST NOT use one of the following as propertyId, otherwise the behavior is 2714 * undefined (might throw exception or might return null): 2715 * 2716 * <ul> 2717 * <li>{@code INITIAL_USER_INFO} 2718 * <li>{@code SWITCH_USER} 2719 * <li>{@code CREATE_USER} 2720 * <li>{@code REMOVE_USER} 2721 * <li>{@code USER_IDENTIFICATION_ASSOCIATION} 2722 * </ul> 2723 * 2724 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} equal 2725 * or later than {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} will receive the following 2726 * exceptions when request failed. 2727 * <ul> 2728 * <li>{@link CarInternalErrorException} when there is an unexpected error detected in cars 2729 * <li>{@link PropertyAccessDeniedSecurityException} when cars denied the access of the 2730 * [propertyId, areaId]. 2731 * <li>{@link PropertyNotAvailableAndRetryException} when the [propertyId, areaId] is 2732 * temporarily not available and likely that retrying will be successful 2733 * <li>{@link PropertyNotAvailableException} when the [propertyId, areaId] is not available 2734 * and might be unavailable for a while. 2735 * <li>{@link IllegalArgumentException} when the [propertyId, areaId] is not supported or 2736 * when the specified class does not match the property type. 2737 * </ul> 2738 * 2739 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} equal 2740 * or later than {@link Build.VERSION_CODES#R}, before 2741 * {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} will 2742 * receive the following exceptions or {@code null} when request failed. 2743 * <ul> 2744 * <li>{@link CarInternalErrorException} when there is an unexpected error detected in cars 2745 * <li>{@link PropertyAccessDeniedSecurityException} when cars denied the access of the 2746 * [propertyId, areaId]. 2747 * <li>{@link PropertyNotAvailableAndRetryException} when the [propertyId, areaId] is 2748 * temporarily not available and likely that retrying will be successful 2749 * <li>{@link PropertyNotAvailableException} when the [propertyId, areaId] is not available 2750 * and might be unavailable for a while. 2751 * <li>{@link IllegalArgumentException} when the specified class does not match the property 2752 * type. 2753 * <li>{@code null} when the [propertyId, areaId] is not supported 2754 * </ul> 2755 * 2756 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} 2757 * earlier than {@link Build.VERSION_CODES#R} will receive the following exceptions or 2758 * {@code null} when request failed. 2759 * <ul> 2760 * <li>{@link IllegalStateException} when there is an error detected in cars, or when 2761 * cars denied the access of the [propertyId, areaId], or when the [propertyId, areaId] 2762 * is not available and might be unavailable for a while, or when unexpected error 2763 * happens. 2764 * <li>{@link IllegalArgumentException} when the specified class does not match the 2765 * property type. 2766 * <li>{@code null} when the [propertyId, areaId] is not supported or when the property is 2767 * temporarily not available. 2768 * </ul> 2769 * 2770 * <p>For pre-R client, the returned value might be null if the property is temporarily not 2771 * available. The client should try again in this case. 2772 * 2773 * <p>For pre-U client, when the [propertyId, areaId] is not supported, this will return 2774 * {@code null}. 2775 * 2776 * <p>For pre-U client, the returned {@link CarPropertyValue} might contain unavailable or 2777 * error status. Client must use {@link CarPropertyValue#getStatus} to check. If the returned 2778 * status is not {@link CarPropertyValue#STATUS_AVAILABLE}, then the value returned via 2779 * {@link CarPropertyValue#getValue} is undefined. 2780 * 2781 * <p>For U and later client, when the [propertyId, areaId] is not supported, this is 2782 * guaranteed to throw {@code IllegalArgumentException}. This method will never return 2783 * {@code null}. 2784 * 2785 * <p>For U and later client, if the property's status is 2786 * {@link CarPropertyValue#STATUS_UNAVAILABLE}, then {@link PropertyNotAvailableException} will 2787 * be thrown. If the property's status is {@link CarPropertyValue#STATUS_ERROR}, then 2788 * {@link CarInternalErrorException} will be thrown. If no exception is thrown, the returned 2789 * {@link CarPropertyValue#getStatus} is guaranteed to be 2790 * {@link CarPropertyValue#STATUS_AVAILABLE} so client do not need to check. 2791 * 2792 * @param propertyId the property ID to get 2793 * @param areaId the area ID of the property to get 2794 * @param <E> the class type of the property 2795 * 2796 * @throws IllegalStateException when there is an error detected in cars, or when cars denied 2797 * the access of the [propertyId, areaId], or when the [propertyId, areaId] is not available and 2798 * might be unavailable for a while, or when unexpected error happens for pre-R clients. 2799 * @throws CarInternalErrorException when there is an unexpected error detected in cars for 2800 * R and later clients. 2801 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the 2802 * property for R and later clients. 2803 * @throws PropertyNotAvailableAndRetryException when [propertyId, areaId] is temporarily 2804 * not available and likely that retrying will be successful for R and later clients. 2805 * @throws PropertyNotAvailableException when [propertyId, areaId] is not available and might be 2806 * unavailable for a while for R and later clients. 2807 * @throws IllegalArgumentException when the [propertyId, areaId] is not supported for U and 2808 * later client, or when the specified class does not match the property type. 2809 * @throws SecurityException when the client does not have the required read permission to 2810 * access the [propertyId, areaId]. 2811 * 2812 * @return the value of a property 2813 */ 2814 @Nullable getProperty(int propertyId, int areaId)2815 public <E> CarPropertyValue<E> getProperty(int propertyId, int areaId) { 2816 if (DBG) { 2817 Slogf.d(TAG, "getProperty, propertyId: %s, areaId: %s", 2818 VehiclePropertyIds.toString(propertyId), toAreaIdString(propertyId, areaId)); 2819 } 2820 2821 assertNotUserHalProperty(propertyId); 2822 2823 try { 2824 assertPropertyIdIsSupported(propertyId); 2825 } catch (IllegalArgumentException e) { 2826 if (mAppTargetSdk >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 2827 throw e; 2828 } else { 2829 // Return null for pre-U unsupported [propertyId, areaId]. 2830 return null; 2831 } 2832 } 2833 2834 Trace.beginSection("getProperty-" + propertyId + "/" + areaId); 2835 try { 2836 CarPropertyValue<E> carPropertyValue = (CarPropertyValue<E>) (runSyncOperation(() -> { 2837 return mService.getProperty(propertyId, areaId); 2838 })); 2839 if (carPropertyValue == null) { 2840 return null; 2841 } 2842 if (mAppTargetSdk >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 2843 if (carPropertyValue.getStatus() == CarPropertyValue.STATUS_UNAVAILABLE) { 2844 throw new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE, 2845 "getProperty returned value with UNAVAILABLE status: " 2846 + carPropertyValue); 2847 } else if (carPropertyValue.getStatus() != CarPropertyValue.STATUS_AVAILABLE) { 2848 throw new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR, 2849 "getProperty returned value with error or unknown status: " 2850 + carPropertyValue); 2851 } 2852 } 2853 return carPropertyValue; 2854 } catch (IllegalArgumentException e) { 2855 if (mAppTargetSdk >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 2856 throw e; 2857 } else { 2858 // Return null for pre-U unsupported [propertyId, areaId]. 2859 return null; 2860 } 2861 } catch (RemoteException e) { 2862 return handleRemoteExceptionFromCarService(e, null); 2863 } catch (ServiceSpecificException e) { 2864 if (DBG) { 2865 Slog.d(TAG, "getProperty received service specific exception, code: " 2866 + e.errorCode); 2867 } 2868 if (mAppTargetSdk < Build.VERSION_CODES.R) { 2869 if (e.errorCode == VehicleHalStatusCode.STATUS_TRY_AGAIN) { 2870 return null; 2871 } else { 2872 throw new IllegalStateException("Failed to get propertyId: " 2873 + VehiclePropertyIds.toString(propertyId) + " areaId: " 2874 + toAreaIdString(propertyId, areaId), e); 2875 } 2876 } 2877 handleCarServiceSpecificException(e, propertyId, areaId); 2878 2879 // Never reaches here. 2880 return null; 2881 } finally { 2882 Trace.endSection(); 2883 } 2884 } 2885 2886 /** 2887 * Set value of car property by areaId. 2888 * 2889 * <p>If multiple clients set a property for the same area ID simultaneously, which one takes 2890 * precedence is undefined. Typically, the last set operation (in the order that they are issued 2891 * to the car's ECU) overrides the previous set operations. 2892 * 2893 * <p>This method may take couple seconds to complete, so it needs to be called form a 2894 * non-main thread. 2895 * 2896 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} equal 2897 * or later than {@link Build.VERSION_CODES#R} will receive the following exceptions when 2898 * request failed. 2899 * <ul> 2900 * <li>{@link CarInternalErrorException} 2901 * <li>{@link PropertyAccessDeniedSecurityException} 2902 * <li>{@link PropertyNotAvailableAndRetryException} 2903 * <li>{@link PropertyNotAvailableException} 2904 * <li>{@link IllegalArgumentException} 2905 * </ul> 2906 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} 2907 * earlier than {@link Build.VERSION_CODES#R} will receive the following exceptions when request 2908 * failed. 2909 * <ul> 2910 * <li>{@link RuntimeException} when the [propertyId, areaId] is temporarily not available. 2911 * <li>{@link IllegalStateException} when there is an error detected in cars, or when 2912 * cars denied the access of the [propertyId, areaId], or when the [property, areaId} is 2913 * not available and might be unavailable for a while, or when unexpected error happens. 2914 * <li>{@link IllegalArgumentException} when the [propertyId, areaId] is not supported. 2915 * </ul> 2916 * 2917 * <p>Returning from this method does not necessary mean the set operation succeeded. In order 2918 * to determine whether the operation succeeded/failed, Client should use 2919 * {@link CarPropertyManager#registerCallback} to register for property updates for this 2920 * [propertyId, areaId] before the set operation. The operation succeeded when 2921 * {@link CarPropertyEventCallback#onChangeEvent} is called with the value to be set. The 2922 * operation failed when {@link CarPropertyEventCallback#onErrorEvent} is called for this 2923 * [propertyId, areaId]. Note that the registration must happen before the set operation 2924 * otherwise the callback might be invoked after the set operation, but before the registration. 2925 * 2926 * 2927 * <p>Note that if the value to set is the same as the current value, the set request will 2928 * still be sent to vehicle hardware, however, a new property change event will not be 2929 * generated for the set operation. If client want to prevent the set request to be sent, 2930 * client must use {@link getProperty} to check the current value before calling this. 2931 * 2932 * @param clazz the class object for the CarPropertyValue 2933 * @param propertyId the property ID to modify 2934 * @param areaId the area ID to apply the modification 2935 * @param val the value to set 2936 * @param <E> the class type of the given property, for example property that was 2937 * defined as {@code VEHICLE_VALUE_TYPE_INT32} in vehicle HAL could be accessed using 2938 * {@code Integer.class}. 2939 * 2940 * @throws RuntimeException when the [propertyId, areaId] is temporarily not available for 2941 * pre-R clients. 2942 * @throws IllegalStateException when there is an error detected in cars, or when cars denied 2943 * the access of the [propertyId, areaId], or when the [propertyId, areaId] is not available and 2944 * might be unavailable for a while, or when unexpected error happens for pre-R clients. 2945 * @throws CarInternalErrorException when there is an unexpected error detected in cars for 2946 * R and later clients. 2947 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the property 2948 * for R and later clients. 2949 * @throws PropertyNotAvailableException when [propertyId, areaId] is not available and might be 2950 * unavailable for a while for R and later clients. 2951 * @throws PropertyNotAvailableAndRetryException when [propertyId, areaId] is temporarily not 2952 * available and likely that retrying will be successful for R and later clients. 2953 * @throws IllegalArgumentException when the [propertyId, areaId] or value is not supported. 2954 * @throws SecurityException when the client does not have the required write permission to 2955 * access the [propertyId, areaId]. 2956 */ setProperty(@onNull Class<E> clazz, int propertyId, int areaId, @NonNull E val)2957 public <E> void setProperty(@NonNull Class<E> clazz, int propertyId, int areaId, 2958 @NonNull E val) { 2959 if (DBG) { 2960 Slogf.d(TAG, "setProperty, propertyId: %s, areaId: %s, class: %s, val: %s", 2961 VehiclePropertyIds.toString(propertyId), toAreaIdString(propertyId, areaId), 2962 clazz, val); 2963 } 2964 2965 assertNotUserHalProperty(propertyId); 2966 2967 assertPropertyIdIsSupported(propertyId); 2968 2969 Trace.beginSection("setProperty-" + propertyId + "/" + areaId); 2970 try { 2971 runSyncOperation(() -> { 2972 mService.setProperty(new CarPropertyValue<>(propertyId, areaId, val), 2973 mCarPropertyEventToService); 2974 return null; 2975 }); 2976 } catch (RemoteException e) { 2977 handleRemoteExceptionFromCarService(e); 2978 } catch (ServiceSpecificException e) { 2979 if (DBG) { 2980 Slog.d(TAG, "setProperty received service specific exception", e); 2981 } 2982 if (mAppTargetSdk < Build.VERSION_CODES.R) { 2983 if (e.errorCode == VehicleHalStatusCode.STATUS_TRY_AGAIN) { 2984 throw new RuntimeException("Failed to set propertyId: " 2985 + VehiclePropertyIds.toString(propertyId) + " areaId: " 2986 + toAreaIdString(propertyId, areaId), e); 2987 } else { 2988 throw new IllegalStateException("Failed to set propertyId: " 2989 + VehiclePropertyIds.toString(propertyId) + " areaId: " 2990 + toAreaIdString(propertyId, areaId), e); 2991 } 2992 } 2993 handleCarServiceSpecificException(e, propertyId, areaId); 2994 } finally { 2995 Trace.endSection(); 2996 } 2997 } 2998 2999 /** 3000 * Modifies a property. If the property modification doesn't occur, an error event shall be 3001 * generated and propagated back to the application. 3002 * 3003 * <p>This method may take couple seconds to complete, so it needs to be called from a 3004 * non-main thread. 3005 * 3006 * @param propertyId the property ID to modify 3007 * @param areaId the area ID to apply the modification 3008 * @param val the value to set 3009 * 3010 * @throws RuntimeException when the [propertyId, areaId] is temporarily not available for 3011 * pre-R clients. 3012 * @throws IllegalStateException when there is an error detected in cars, or when cars denied 3013 * the access of the [propertyId, areaId], or when the [propertyId, areaId] is not available and 3014 * might be unavailable for a while, or when unexpected error happens for pre-R clients. 3015 * @throws CarInternalErrorException when there is an unexpected error detected in cars for 3016 * R and later clients. 3017 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the property 3018 * for R and later clients. 3019 * @throws PropertyNotAvailableException when [propertyId, areaId] is not available and might be 3020 * unavailable for a while for R and later clients. 3021 * @throws PropertyNotAvailableAndRetryException when [propertyId, areaId] is temporarily not 3022 * available and likely that retrying will be successful for R and later clients. 3023 * @throws IllegalArgumentException when the [propertyId, areaId] or value is not supported. 3024 * @throws SecurityException when the client does not have the required write permission to 3025 * access the [propertyId, areaId]. 3026 */ setBooleanProperty(int propertyId, int areaId, boolean val)3027 public void setBooleanProperty(int propertyId, int areaId, boolean val) { 3028 setProperty(Boolean.class, propertyId, areaId, val); 3029 } 3030 3031 /** 3032 * Set float value of property 3033 * 3034 * <p>This method may take couple seconds to complete, so it needs to be called from a 3035 * non-main thread. 3036 * 3037 * @param propertyId the property ID to modify 3038 * @param areaId the area ID to apply the modification 3039 * @param val the value to set 3040 * 3041 * @throws RuntimeException when the [propertyId, areaId] is temporarily not available for 3042 * pre-R clients. 3043 * @throws IllegalStateException when there is an error detected in cars, or when cars denied 3044 * the access of the [propertyId, areaId], or when the [propertyId, areaId] is not available and 3045 * might be unavailable for a while, or when unexpected error happens for pre-R clients. 3046 * @throws CarInternalErrorException when there is an unexpected error detected in cars for 3047 * R and later clients. 3048 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the property 3049 * for R and later clients. 3050 * @throws PropertyNotAvailableException when [propertyId, areaId] is not available and might be 3051 * unavailable for a while for R and later clients. 3052 * @throws PropertyNotAvailableAndRetryException when [propertyId, areaId] is temporarily not 3053 * available and likely that retrying will be successful for R and later clients. 3054 * @throws IllegalArgumentException when the [propertyId, areaId] or value is not supported. 3055 * @throws SecurityException when the client does not have the required write permission to 3056 * access the [propertyId, areaId]. 3057 */ setFloatProperty(int propertyId, int areaId, float val)3058 public void setFloatProperty(int propertyId, int areaId, float val) { 3059 setProperty(Float.class, propertyId, areaId, val); 3060 } 3061 3062 /** 3063 * Set int value of property 3064 * 3065 * <p>This method may take couple seconds to complete, so it needs to be called from a 3066 * non-main thread. 3067 * 3068 * @param propertyId the property ID to modify 3069 * @param areaId the area ID to apply the modification 3070 * @param val the value to set 3071 * 3072 * @throws RuntimeException when the [propertyId, areaId] is temporarily not available for 3073 * pre-R clients. 3074 * @throws IllegalStateException when there is an error detected in cars, or when cars denied 3075 * the access of the [propertyId, areaId], or when the [propertyId, areaId] is not available and 3076 * might be unavailable for a while, or when unexpected error happens for pre-R clients. 3077 * @throws CarInternalErrorException when there is an unexpected error detected in cars for 3078 * R and later clients. 3079 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the property 3080 * for R and later clients. 3081 * @throws PropertyNotAvailableException when [propertyId, areaId] is not available and might be 3082 * unavailable for a while for R and later clients. 3083 * @throws PropertyNotAvailableAndRetryException when [propertyId, areaId] is temporarily not 3084 * available and likely that retrying will be successful for R and later clients. 3085 * @throws IllegalArgumentException when the [propertyId, areaId] or value is not supported. 3086 * @throws SecurityException when the client does not have the required write permission to 3087 * access the [propertyId, areaId]. 3088 */ setIntProperty(int propertyId, int areaId, int val)3089 public void setIntProperty(int propertyId, int areaId, int val) { 3090 setProperty(Integer.class, propertyId, areaId, val); 3091 } 3092 3093 /** 3094 * Handles {@code ServiceSpecificException} in {@code CarService} for R and later version. 3095 */ handleCarServiceSpecificException( ServiceSpecificException e, int propertyId, int areaId)3096 private void handleCarServiceSpecificException( 3097 ServiceSpecificException e, int propertyId, int areaId) { 3098 // We are not passing the error message down, so log it here. 3099 Slog.w(TAG, "received ServiceSpecificException: " + e); 3100 // The e.errorCode here is not 0. This always throws a CarInternalErrorException or 3101 // PropertyNotAvailableException. 3102 CarPropertyErrorCodes.createFromVhalStatusCode(e.errorCode) 3103 .checkAndMaybeThrowException(propertyId, areaId); 3104 } 3105 clearRequestIdToAsyncRequestInfo( List<? extends AsyncPropertyRequest> asyncPropertyRequests)3106 private void clearRequestIdToAsyncRequestInfo( 3107 List<? extends AsyncPropertyRequest> asyncPropertyRequests) { 3108 if (DBG) { 3109 Slog.d(TAG, "clear pending async requests: " + asyncPropertyRequests); 3110 } 3111 synchronized (mLock) { 3112 for (int i = 0; i < asyncPropertyRequests.size(); i++) { 3113 mRequestIdToAsyncRequestInfo.remove(asyncPropertyRequests.get(i).getRequestId()); 3114 } 3115 } 3116 } 3117 3118 /** 3119 * Set an {@code onCancelListener} for the cancellation signal. 3120 * 3121 * <p>When the signal is cancelled, car service will remove the stored state for the specified 3122 * pending request IDs and ignore all the future results. 3123 */ setOnCancelListener(CancellationSignal cancellationSignal, List<Integer> requestIds)3124 private void setOnCancelListener(CancellationSignal cancellationSignal, 3125 List<Integer> requestIds) { 3126 cancellationSignal.setOnCancelListener(() -> { 3127 int[] requestIdsArray = new int[requestIds.size()]; 3128 synchronized (mLock) { 3129 for (int i = 0; i < requestIds.size(); i++) { 3130 int requestId = requestIds.get(i); 3131 requestIdsArray[i] = requestId; 3132 mRequestIdToAsyncRequestInfo.remove(requestId); 3133 } 3134 } 3135 try { 3136 mService.cancelRequests(requestIdsArray); 3137 } catch (RemoteException e) { 3138 handleRemoteExceptionFromCarService(e); 3139 } 3140 }); 3141 } 3142 3143 /** @hide */ 3144 @Override onCarDisconnected()3145 public void onCarDisconnected() { 3146 synchronized (mLock) { 3147 mCpeCallbackToCpeCallbackController.clear(); 3148 mPropIdToCpeCallbackControllerList.clear(); 3149 mSubscriptionManager.clear(); 3150 mSupportedValuesChangeCallbackByPropIdAreaId.clear(); 3151 mExecutorBySupportedValuesChangeCallback.clear(); 3152 mPropIdAreaIdsBySupportedValuesChangeCallback.clear(); 3153 } 3154 } 3155 3156 /** 3157 * Generate unique get request ID and return to the client. 3158 * 3159 * @param propertyId the property ID 3160 * @param areaId the area ID 3161 * @return the GetPropertyRequest object 3162 */ 3163 @NonNull 3164 @SuppressWarnings("FormatString") generateGetPropertyRequest(int propertyId, int areaId)3165 public GetPropertyRequest generateGetPropertyRequest(int propertyId, int areaId) { 3166 int requestIdCounter = mRequestIdCounter.getAndIncrement(); 3167 if (DBG) { 3168 Slogf.d(TAG, "generateGetPropertyRequest, requestId: %d, propertyId: %s, " 3169 + "areaId: %s", requestIdCounter, VehiclePropertyIds.toString(propertyId), 3170 toAreaIdString(propertyId, areaId)); 3171 } 3172 return new GetPropertyRequest(requestIdCounter, propertyId, areaId); 3173 } 3174 3175 /** 3176 * Generate unique set request ID and return to the client. 3177 * 3178 * @param <T> the type for the property value, must be one of Object, Boolean, Float, Integer, 3179 * Long, Float[], Integer[], Long[], String, byte[], Object[] 3180 * @param propertyId the property ID 3181 * @param areaId the area ID 3182 * @param value the value to set 3183 * @return the {@link SetPropertyRequest} object 3184 */ 3185 @NonNull 3186 @SuppressWarnings("FormatString") generateSetPropertyRequest(int propertyId, int areaId, @NonNull T value)3187 public <T> SetPropertyRequest<T> generateSetPropertyRequest(int propertyId, int areaId, 3188 @NonNull T value) { 3189 requireNonNull(value); 3190 int requestIdCounter = mRequestIdCounter.getAndIncrement(); 3191 if (DBG) { 3192 Slogf.d(TAG, "generateSetPropertyRequest, requestId: %d, propertyId: %s, " 3193 + "areaId: %s, value: %s", requestIdCounter, 3194 VehiclePropertyIds.toString(propertyId), toAreaIdString(propertyId, areaId), 3195 value); 3196 } 3197 return new SetPropertyRequest(requestIdCounter, propertyId, areaId, value); 3198 } 3199 checkAsyncArguments(Object requests, Object callback, long timeoutInMs)3200 private void checkAsyncArguments(Object requests, Object callback, long timeoutInMs) { 3201 requireNonNull(requests); 3202 requireNonNull(callback); 3203 if (timeoutInMs <= 0) { 3204 throw new IllegalArgumentException("timeoutInMs must be a positive number"); 3205 } 3206 } 3207 3208 /** 3209 * Query a list of {@link CarPropertyValue} with property ID and area ID asynchronously. 3210 * 3211 * <p>This function would return immediately before the results are ready. For each request, 3212 * the corresponding result would either be delivered through one 3213 * {@code resultCallback.onSuccess} call if the request succeeded or through one 3214 * {@code errorCallback.onFailure} call if failed. It is guaranteed that the total times the 3215 * callback functions are called is equal to the number of requests if this function does not 3216 * throw an exception. It is guaranteed that none of the callback functions are called if an 3217 * exception is thrown. If the {@code callbackExecutor} is {@code null}, the callback will be 3218 * executed on the default event handler thread. If the callback is doing heavy work, it is 3219 * recommended that the {@code callbackExecutor} is provided. 3220 * 3221 * <p>If the operation is cancelled, it is guaranteed that no more callbacks will be called. 3222 * 3223 * <p>For one request, if the property's status is not available, 3224 * {@code errorCallback.onFailure} will be called once with {@link #STATUS_ERROR_NOT_AVAILABLE}. 3225 * 3226 * <p>For one request, if the property's status is error, 3227 * {@code errorCallback.onFailure} will be called once with {@link 3228 * #STATUS_ERROR_INTERNAL_ERROR}. 3229 * 3230 * @param getPropertyRequests a list of properties to get 3231 * @param timeoutInMs the timeout for the operation, in milliseconds 3232 * @param cancellationSignal a signal that could be used to cancel the on-going operation 3233 * @param callbackExecutor the executor to execute the callback with 3234 * @param getPropertyCallback the callback function to deliver the result 3235 * @throws SecurityException if missing permission to read one of the specific properties. 3236 * @throws IllegalArgumentException if one of the properties to read is not supported. 3237 */ getPropertiesAsync( @onNull List<GetPropertyRequest> getPropertyRequests, long timeoutInMs, @Nullable CancellationSignal cancellationSignal, @Nullable @CallbackExecutor Executor callbackExecutor, @NonNull GetPropertyCallback getPropertyCallback)3238 public void getPropertiesAsync( 3239 @NonNull List<GetPropertyRequest> getPropertyRequests, 3240 long timeoutInMs, 3241 @Nullable CancellationSignal cancellationSignal, 3242 @Nullable @CallbackExecutor Executor callbackExecutor, 3243 @NonNull GetPropertyCallback getPropertyCallback) { 3244 if (DBG) { 3245 Slogf.d(TAG, "getPropertiesAsync, requests: %s, timeoutInMs: %d, callback: %s", 3246 getPropertyRequests, timeoutInMs, getPropertyCallback); 3247 } 3248 3249 checkAsyncArguments(getPropertyRequests, getPropertyCallback, timeoutInMs); 3250 if (callbackExecutor == null) { 3251 callbackExecutor = new HandlerExecutor(getEventHandler()); 3252 } 3253 3254 List<AsyncPropertyServiceRequest> getPropertyServiceRequests = new ArrayList<>( 3255 getPropertyRequests.size()); 3256 for (int i = 0; i < getPropertyRequests.size(); i++) { 3257 GetPropertyRequest getPropertyRequest = getPropertyRequests.get(i); 3258 int propertyId = getPropertyRequest.getPropertyId(); 3259 assertPropertyIdIsSupported(propertyId); 3260 3261 getPropertyServiceRequests.add(AsyncPropertyServiceRequest.newGetAsyncRequest( 3262 getPropertyRequest)); 3263 } 3264 3265 List<Integer> requestIds = storePendingRequestInfo(getPropertyRequests, callbackExecutor, 3266 getPropertyCallback); 3267 3268 try { 3269 if (DBG) { 3270 Slog.d(TAG, "calling CarPropertyService.getPropertiesAsync"); 3271 } 3272 mService.getPropertiesAsync(new AsyncPropertyServiceRequestList( 3273 getPropertyServiceRequests), mAsyncPropertyResultCallback, timeoutInMs); 3274 if (DBG) { 3275 Slog.d(TAG, "CarPropertyService.getPropertiesAsync succeed"); 3276 } 3277 } catch (RemoteException e) { 3278 clearRequestIdToAsyncRequestInfo(getPropertyRequests); 3279 handleRemoteExceptionFromCarService(e); 3280 } catch (IllegalArgumentException | SecurityException e) { 3281 clearRequestIdToAsyncRequestInfo(getPropertyRequests); 3282 throw e; 3283 } 3284 if (cancellationSignal != null) { 3285 setOnCancelListener(cancellationSignal, requestIds); 3286 } 3287 } 3288 3289 /** 3290 * Query a list of {@link CarPropertyValue} with property Id and area Id asynchronously. 3291 * 3292 * Same as {@link CarPropertyManager#getPropertiesAsync(List, long, CancellationSignal, 3293 * Executor, GetPropertyCallback)} with default timeout 10s. 3294 */ getPropertiesAsync( @onNull List<GetPropertyRequest> getPropertyRequests, @Nullable CancellationSignal cancellationSignal, @Nullable @CallbackExecutor Executor callbackExecutor, @NonNull GetPropertyCallback getPropertyCallback)3295 public void getPropertiesAsync( 3296 @NonNull List<GetPropertyRequest> getPropertyRequests, 3297 @Nullable CancellationSignal cancellationSignal, 3298 @Nullable @CallbackExecutor Executor callbackExecutor, 3299 @NonNull GetPropertyCallback getPropertyCallback) { 3300 getPropertiesAsync(getPropertyRequests, ASYNC_GET_DEFAULT_TIMEOUT_MS, cancellationSignal, 3301 callbackExecutor, getPropertyCallback); 3302 } 3303 3304 /** 3305 * Sets a list of car property values asynchronously. 3306 * 3307 * <p>This function would return immediately before the results are ready. For each request, 3308 * the corresponding result would either be delivered through one 3309 * {@code resultCallback.onSuccess} call if the request succeeded or through one 3310 * {@code errorCallback.onFailure} call if failed. It is guaranteed that the total times the 3311 * callback functions are called is equal to the number of requests if this function does not 3312 * throw an exception. It is guaranteed that none of the callback functions are called if an 3313 * exception is thrown. If the {@code callbackExecutor} is {@code null}, the callback will be 3314 * executed on the default event handler thread. If the callback is doing heavy work, it is 3315 * recommended that the {@code callbackExecutor} is provided. 3316 * 3317 * <p>If the operation is cancelled, it is guaranteed that no more callbacks will be called. 3318 * 3319 * <p>If multiple clients set a property for the same area ID simultaneously, which one takes 3320 * precedence is undefined. Typically, the last set operation (in the order that they are issued 3321 * to the car's ECU) overrides the previous set operations. 3322 * 3323 * <p>When the success callback will be called depends on whether {@code waitForPropertyUpdate} 3324 * for each request is set. If this is set to {@code true} (by default), the success callback 3325 * will be called when the set operation is successfully delivered to vehicle bus AND either 3326 * target value is the same as the current or when the property is updated to the target value. 3327 * 3328 * <p>When {@code waitForPropertyUpdate} is set to {@code false}, the success callback will be 3329 * called as long as the set operation is successfully delivered to vehicle bus. 3330 * 3331 * <p>Under most cases, client should wait for the property update to verify that the set 3332 * operation actually succeeded. 3333 * 3334 * <p>For cases when the property is write-only (no way to get property update event) or when 3335 * the property represents some action, instead of an actual state, e.g. key stroke where the 3336 * property's current value is not meaningful, caller must set {@code waitForPropertyUpdate} 3337 * to {@code false}. 3338 * 3339 * <p>For {@code HVAC_TEMPERATURE_VALUE_SUGGESTION}, this must be set to {@code false} 3340 * because the updated property value will not be the same as the value to be set. 3341 * 3342 * @param setPropertyRequests a list of properties to set 3343 * @param timeoutInMs the timeout for the operation, in milliseconds 3344 * @param cancellationSignal a signal that could be used to cancel the on-going operation 3345 * @param callbackExecutor the executor to execute the callback with 3346 * @param setPropertyCallback the callback function to deliver the result 3347 * @throws SecurityException if missing permission to write one of the specific properties. 3348 * @throws IllegalArgumentException if one of the properties to set is not supported. 3349 * @throws IllegalArgumentException if one of the values to set is not supported by the 3350 * property. 3351 * @throws IllegalArgumentException if one of the properties is not readable and does not set 3352 * {@code waitForPropertyUpdate} to {@code false}. 3353 * @throws IllegalArgumentException if one of the properties is 3354 * {@code HVAC_TEMPERATURE_VALUE_SUGGESTION} and does not set {@code waitForPropertyUpdate} 3355 * to {@code false}. 3356 */ setPropertiesAsync( @onNull List<SetPropertyRequest<?>> setPropertyRequests, long timeoutInMs, @Nullable CancellationSignal cancellationSignal, @Nullable @CallbackExecutor Executor callbackExecutor, @NonNull SetPropertyCallback setPropertyCallback)3357 public void setPropertiesAsync( 3358 @NonNull List<SetPropertyRequest<?>> setPropertyRequests, 3359 long timeoutInMs, 3360 @Nullable CancellationSignal cancellationSignal, 3361 @Nullable @CallbackExecutor Executor callbackExecutor, 3362 @NonNull SetPropertyCallback setPropertyCallback) { 3363 if (DBG) { 3364 Slogf.d(TAG, "setPropertiesAsync, requests: %s, timeoutInMs: %d, callback: %s", 3365 setPropertyRequests, timeoutInMs, setPropertyCallback); 3366 } 3367 3368 checkAsyncArguments(setPropertyRequests, setPropertyCallback, timeoutInMs); 3369 if (callbackExecutor == null) { 3370 callbackExecutor = new HandlerExecutor(getEventHandler()); 3371 } 3372 3373 List<AsyncPropertyServiceRequest> setPropertyServiceRequests = new ArrayList<>( 3374 setPropertyRequests.size()); 3375 for (int i = 0; i < setPropertyRequests.size(); i++) { 3376 SetPropertyRequest setPropertyRequest = setPropertyRequests.get(i); 3377 int propertyId = setPropertyRequest.getPropertyId(); 3378 requireNonNull(setPropertyRequest.getValue()); 3379 assertPropertyIdIsSupported(propertyId); 3380 3381 setPropertyServiceRequests.add(AsyncPropertyServiceRequest.newSetAsyncRequest( 3382 setPropertyRequest)); 3383 } 3384 3385 List<Integer> requestIds = storePendingRequestInfo(setPropertyRequests, callbackExecutor, 3386 setPropertyCallback); 3387 3388 try { 3389 if (DBG) { 3390 Slog.d(TAG, "calling CarPropertyService.setPropertiesAsync"); 3391 } 3392 mService.setPropertiesAsync(new AsyncPropertyServiceRequestList( 3393 setPropertyServiceRequests), mAsyncPropertyResultCallback, timeoutInMs); 3394 if (DBG) { 3395 Slog.d(TAG, "CarPropertyService.setPropertiesAsync succeed"); 3396 } 3397 } catch (RemoteException e) { 3398 clearRequestIdToAsyncRequestInfo(setPropertyRequests); 3399 handleRemoteExceptionFromCarService(e); 3400 } catch (IllegalArgumentException | SecurityException e) { 3401 clearRequestIdToAsyncRequestInfo(setPropertyRequests); 3402 throw e; 3403 } 3404 if (cancellationSignal != null) { 3405 setOnCancelListener(cancellationSignal, requestIds); 3406 } 3407 } 3408 3409 /** 3410 * Sets a list of car property values asynchronously. 3411 * 3412 * Same as {@link CarPropertyManager#setPropertiesAsync(List, long, CancellationSignal, 3413 * Executor, SetPropertyCallback)} with default timeout 10s. 3414 */ setPropertiesAsync( @onNull List<SetPropertyRequest<?>> setPropertyRequests, @Nullable CancellationSignal cancellationSignal, @Nullable @CallbackExecutor Executor callbackExecutor, @NonNull SetPropertyCallback setPropertyCallback)3415 public void setPropertiesAsync( 3416 @NonNull List<SetPropertyRequest<?>> setPropertyRequests, 3417 @Nullable CancellationSignal cancellationSignal, 3418 @Nullable @CallbackExecutor Executor callbackExecutor, 3419 @NonNull SetPropertyCallback setPropertyCallback) { 3420 setPropertiesAsync(setPropertyRequests, ASYNC_GET_DEFAULT_TIMEOUT_MS, cancellationSignal, 3421 callbackExecutor, setPropertyCallback); 3422 } 3423 3424 /** 3425 * Gets the currently supported min/max value for [propertyId, areaId]. 3426 * 3427 * This is only meaningful if {@link AreaIdConfig#hasMinSupportedValue} or 3428 * {@link AreaIdConfig#hasMaxSupportedValue} returns {@code true}. 3429 * 3430 * <p>Unless mentioned otherwise in property definition, this function is only meaningful 3431 * for int32, int64, float property types. 3432 * 3433 * <p>For certain properties, {@link AreaIdConfig#hasMinSupportedValue} and 3434 * {@link AreaIdConfig#hasMaxSupportedValue} always returns 3435 * {@code true} and you could always use this function to get min/max value in normal cases, 3436 * e.g. {@code EV_BRAKE_REGENERATION_LEVEL}. Check {@link VehiclePropertyIds} documentation for 3437 * more detail. 3438 * 3439 * <p>Note that the returned value range is a super-set applies for both values 3440 * set to vehicle hardware and values read from vehicle hardware. The value 3441 * range may change dynamically so it is still possible to get 3442 * {@link IllegalArgumentException} for 3443 * {@link CarPropertyManager#setProperty} even though the value to set is 3444 * within the value range. 3445 * 3446 * <p>Caller should use {@link CarPropertyManager#registerSupportedValuesChangeCallback} to 3447 * register for supported value change. 3448 * 3449 * @return The currently supported min/max value. 3450 * @throws IllegalArgumentException if [propertyId, areaId] is not supported. 3451 * @throws SecurityException if the caller does not have either read or write access to the 3452 * property. 3453 * @throws CarInternalErrorException if failed to get the information from the hardware. 3454 */ 3455 @FlaggedApi(FLAG_CAR_PROPERTY_SUPPORTED_VALUE) getMinMaxSupportedValue( int propertyId, int areaId)3456 public <T> @NonNull MinMaxSupportedValue<T> getMinMaxSupportedValue( 3457 int propertyId, int areaId) { 3458 assertPropertyIdIsSupported(propertyId); 3459 3460 MinMaxSupportedPropertyValue supportedPropertyValue; 3461 try { 3462 // This throws IllegalArgumentException or SecurityException, we just rethrow. 3463 supportedPropertyValue = mService.getMinMaxSupportedValue(propertyId, areaId); 3464 } catch (RemoteException e) { 3465 return handleRemoteExceptionFromCarService(e, new MinMaxSupportedValue( 3466 /* minValue= */ null, /* maxValue= */ null)); 3467 } catch (ServiceSpecificException e) { 3468 Slog.e(TAG, "Failed to get min max supported value", e); 3469 throw new CarInternalErrorException(propertyId, areaId); 3470 } 3471 3472 T minValue = null; 3473 T maxValue = null; 3474 RawPropertyValue minRawPropertyValue = extractRawPropertyValue( 3475 supportedPropertyValue.minValue); 3476 if (minRawPropertyValue != null) { 3477 minValue = (T) minRawPropertyValue.getTypedValue(); 3478 } 3479 RawPropertyValue maxRawPropertyValue = extractRawPropertyValue( 3480 supportedPropertyValue.maxValue); 3481 if (maxRawPropertyValue != null) { 3482 maxValue = (T) maxRawPropertyValue.getTypedValue(); 3483 } 3484 return new MinMaxSupportedValue(minValue, maxValue); 3485 } 3486 3487 /** 3488 * Gets the currently supported values list for [propertyId, areaId]. 3489 * 3490 * <p>This is only meaningful if {@link AreaIdConfig#hasSupportedValuesList} returns 3491 * {@code true}. 3492 * 3493 * <p>For certain properties, {@link AreaIdConfig#hasSupportedValuesList} always returns 3494 * {@code true} and you could always use this function to get supported values in normal cases, 3495 * e.g. {@code GEAR_SELECTION}. Check {@link VehiclePropertyIds} documentation for 3496 * more detail. 3497 * 3498 * <p>Note that the returned value range is a super-set applies for both values 3499 * set to vehicle hardware and values read from vehicle hardware. The value 3500 * range may change dynamically so it is still possible to get 3501 * {@link IllegalArgumentException} for 3502 * {@link CarPropertyManager#setProperty} even though the value to set is 3503 * within the value range. 3504 * 3505 * <p>Caller should use {@link CarPropertyManager#registerSupportedValuesChangeCallback} to 3506 * register for supported value list change. 3507 * 3508 * <p>The returned supported value list is in sorted ascending order if the property is of 3509 * type int32, int64 or float. 3510 * 3511 * @return The immutable supported values. {@code null} if no supported values are currently 3512 * specified. If this returns an empty set, it means no values are supported now 3513 * (the property is probably in an error state). 3514 * @throws IllegalArgumentException if [propertyId, areaId] is not supported. 3515 * @throws SecurityException if the caller does not have either read or write access to the 3516 * property. 3517 * @throws CarInternalErrorException if failed to get the information from the hardware. 3518 */ 3519 @FlaggedApi(FLAG_CAR_PROPERTY_SUPPORTED_VALUE) getSupportedValuesList(int propertyId, int areaId)3520 public <T> @Nullable List<T> getSupportedValuesList(int propertyId, int areaId) { 3521 assertPropertyIdIsSupported(propertyId); 3522 3523 List<RawPropertyValue> supportedRawPropertyValues; 3524 try { 3525 // This throws IllegalArgumentException or SecurityException, we just rethrow. 3526 supportedRawPropertyValues = mService.getSupportedValuesList(propertyId, areaId); 3527 } catch (RemoteException e) { 3528 return handleRemoteExceptionFromCarService(e, null); 3529 } catch (ServiceSpecificException e) { 3530 Slog.e(TAG, "Failed to get supported values list", e); 3531 throw new CarInternalErrorException(propertyId, areaId); 3532 } 3533 3534 if (supportedRawPropertyValues == null) { 3535 return null; 3536 } 3537 3538 List<T> mutableReturnValues = new ArrayList<T>(); 3539 for (int i = 0; i < supportedRawPropertyValues.size(); i++) { 3540 mutableReturnValues.add((T) supportedRawPropertyValues.get(i).getTypedValue()); 3541 } 3542 // Returns an immutable list. 3543 return List.copyOf(mutableReturnValues); 3544 } 3545 3546 /** 3547 * A callback interface to deliver value range change callbacks. 3548 */ 3549 @FlaggedApi(FLAG_CAR_PROPERTY_SUPPORTED_VALUE) 3550 public interface SupportedValuesChangeCallback { 3551 /** 3552 * Called when the result for {@link CarPropertyManager#getMinMaxSupportedValue} or 3553 * {@link CarPropertyManager#getSupportedValuesList} change for the [propertyId, areaId]. 3554 * 3555 * <p>Caller should call the listed APIs to refresh. 3556 * 3557 * @param propertyId The property ID. 3558 * @param areaId The area ID. 3559 */ onSupportedValuesChange(int propertyId, int areaId)3560 void onSupportedValuesChange(int propertyId, int areaId); 3561 } 3562 3563 /** 3564 * Registers a callback that will be called when min or max or supported value list for any 3565 * areaIds for the propertyId changes. 3566 * 3567 * <p>If a different callback was previously registered for this property, this adds a new 3568 * callback. 3569 * 3570 * <p>The callback will be executed on the event handler provided to the 3571 * {@link android.car.Car} or the main thread if none was provided. 3572 * 3573 * @param propertyId The property ID. 3574 * @param cb The callback to deliver value range change events. 3575 * @return {@code true} if registered successfully. 3576 * @throws IllegalArgumentException if the property ID is not supported. 3577 */ 3578 @FlaggedApi(FLAG_CAR_PROPERTY_SUPPORTED_VALUE) registerSupportedValuesChangeCallback(int propertyId, @NonNull SupportedValuesChangeCallback cb)3579 public boolean registerSupportedValuesChangeCallback(int propertyId, 3580 @NonNull SupportedValuesChangeCallback cb) { 3581 return registerSupportedValuesChangeCallbackInternal(propertyId, 3582 /* callbackExecutor= */ null, cb); 3583 } 3584 3585 /** 3586 * Registers a callback that will be called when min or max or supported value list for any 3587 * areaIds for the propertyId changes. 3588 * 3589 * <p>One callback must only be associated with one executor. 3590 * 3591 * <p>If a different callback was previously registered for this property, this adds a new 3592 * callback. 3593 * 3594 * @param propertyId The property ID. 3595 * @param callbackExecutor The executor in which the callback is done on. One callback is only 3596 * allowed to be associated with one executor. 3597 * @param cb The callback to deliver value range change events. 3598 * @return {@code true} if registered successfully. 3599 * @throws IllegalArgumentException if the property ID is not supported. 3600 * @throws IllegalArgumentException if the callback was previously associated with a different 3601 * executor. 3602 */ 3603 @FlaggedApi(FLAG_CAR_PROPERTY_SUPPORTED_VALUE) registerSupportedValuesChangeCallback(int propertyId, @NonNull @CallbackExecutor Executor callbackExecutor, @NonNull SupportedValuesChangeCallback cb)3604 public boolean registerSupportedValuesChangeCallback(int propertyId, 3605 @NonNull @CallbackExecutor Executor callbackExecutor, 3606 @NonNull SupportedValuesChangeCallback cb) { 3607 requireNonNull(callbackExecutor); 3608 return registerSupportedValuesChangeCallbackInternal(propertyId, callbackExecutor, cb); 3609 } 3610 3611 /** 3612 * Registers a callback that will be called when min or max or supported value list for 3613 * [propertyId, areaId] changes. 3614 * 3615 * <p>If a different callback was previously registered for [propertyId, areaId], this adds a 3616 * new callback. 3617 * 3618 * <p>The callback will be executed on the event handler provided to the 3619 * {@link android.car.Car} or the main thread if none was provided. 3620 * 3621 * @param propertyId The property ID. 3622 * @param areaId The area ID. 3623 * @param cb The callback to deliver value range change events. 3624 * @return {@code true} if registers successfully. 3625 * @throws IllegalArgumentException if [propertyId, areaId] is not supported. 3626 */ 3627 @FlaggedApi(FLAG_CAR_PROPERTY_SUPPORTED_VALUE) registerSupportedValuesChangeCallback(int propertyId, int areaId, @NonNull SupportedValuesChangeCallback cb)3628 public boolean registerSupportedValuesChangeCallback(int propertyId, int areaId, 3629 @NonNull SupportedValuesChangeCallback cb) { 3630 return registerSupportedValuesChangeCallbackInternal(propertyId, areaId, 3631 /* callbackExecutor= */ null, cb); 3632 } 3633 3634 /** 3635 * Registers a callback that will be called when min or max or supported value list for 3636 * [propertyId, areaId] changes. 3637 * 3638 * <p>One callback must only be associated with one executor. 3639 * 3640 * <p>If a different callback was previously registered for [propertyId, areaId], this adds a 3641 * new callback. 3642 * 3643 * @param propertyId The property ID. 3644 * @param areaId The area ID. 3645 * @param callbackExecutor The executor in which the callback is done on. 3646 * @param cb The callback to deliver value range change events. 3647 * @return {@code true} if registers successfully. 3648 * @throws IllegalArgumentException if [propertyId, areaId] is not supported. 3649 * @throws IllegalArgumentException if the callback was previously associated with a different 3650 * executor. 3651 */ 3652 @FlaggedApi(FLAG_CAR_PROPERTY_SUPPORTED_VALUE) registerSupportedValuesChangeCallback(int propertyId, int areaId, @NonNull @CallbackExecutor Executor callbackExecutor, @NonNull SupportedValuesChangeCallback cb)3653 public boolean registerSupportedValuesChangeCallback(int propertyId, int areaId, 3654 @NonNull @CallbackExecutor Executor callbackExecutor, 3655 @NonNull SupportedValuesChangeCallback cb) { 3656 requireNonNull(callbackExecutor); 3657 return registerSupportedValuesChangeCallbackInternal(propertyId, areaId, callbackExecutor, 3658 cb); 3659 } 3660 3661 /** 3662 * Unregisters all value range change callbacks for the property ID 3663 * 3664 * <p>Do nothing if no callbacks was registered before. 3665 * 3666 * @param propertyId The property ID. 3667 * @throws IllegalArgumentException if the propertyId is not supported. 3668 */ 3669 @FlaggedApi(FLAG_CAR_PROPERTY_SUPPORTED_VALUE) unregisterSupportedValuesChangeCallback(int propertyId)3670 public void unregisterSupportedValuesChangeCallback(int propertyId) { 3671 synchronized (mLock) { 3672 var areaIds = mSupportedValuesChangeCallbackByPropIdAreaId.getSecondKeysForFirstKey( 3673 propertyId); 3674 if (areaIds.isEmpty()) { 3675 Slogf.d(TAG, "No SupportedValuesChangeCallback was registered for property: " 3676 + "%s, do nothing", VehiclePropertyIds.toString(propertyId)); 3677 return; 3678 } 3679 List<PropIdAreaId> propIdAreaIds = new ArrayList<>(); 3680 for (int i = 0; i < areaIds.size(); i++) { 3681 var areaId = areaIds.valueAt(i); 3682 propIdAreaIds.add(newPropIdAreaId(propertyId, areaId)); 3683 var registeredCallbacks = mSupportedValuesChangeCallbackByPropIdAreaId.get( 3684 propertyId, areaId); 3685 for (int j = 0; j < registeredCallbacks.size(); j++) { 3686 var registeredCallback = registeredCallbacks.valueAt(j); 3687 var registeredPropIdAreaIdsForCallback = 3688 mPropIdAreaIdsBySupportedValuesChangeCallback.get(registeredCallback); 3689 if (registeredPropIdAreaIdsForCallback == null) { 3690 Slogf.e(TAG, "No registered propIdAreaId for " 3691 + "supportedValuesChangeCallback: %s, must not happen should at " 3692 + "least contain property: %s, areaId: %s", 3693 registeredCallback, VehiclePropertyIds.toString(propertyId), 3694 toAreaIdString(propertyId, areaId)); 3695 continue; 3696 } 3697 registeredPropIdAreaIdsForCallback.remove(newPropIdAreaId(propertyId, areaId)); 3698 if (registeredPropIdAreaIdsForCallback.isEmpty()) { 3699 // There is no more [propId, areaId]s registered for the callback, we can 3700 // now unlink the executor. 3701 mExecutorBySupportedValuesChangeCallback.remove(registeredCallback); 3702 mPropIdAreaIdsBySupportedValuesChangeCallback.remove(registeredCallback); 3703 } 3704 } 3705 } 3706 for (int i = 0; i < areaIds.size(); i++) { 3707 mSupportedValuesChangeCallbackByPropIdAreaId.remove(propertyId, areaIds.valueAt(i)); 3708 } 3709 3710 // Even though this involves a binder call, we call this inside the lock so that this 3711 // whole block does not overlap with another unregister or register operation. 3712 unregisterSupportedValuesChangeCbToCarService(propIdAreaIds); 3713 } 3714 } 3715 3716 /** 3717 * Unregisters the specific callback for the property ID. 3718 * 3719 * <p>Do nothing if the callback was not registered before. 3720 * 3721 * @param propertyId The property ID. 3722 * @param cb The callback to unregister. 3723 * @throws IllegalArgumentException if the propertyId is not supported. 3724 */ 3725 @FlaggedApi(FLAG_CAR_PROPERTY_SUPPORTED_VALUE) unregisterSupportedValuesChangeCallback(int propertyId, @NonNull SupportedValuesChangeCallback cb)3726 public void unregisterSupportedValuesChangeCallback(int propertyId, 3727 @NonNull SupportedValuesChangeCallback cb) { 3728 synchronized (mLock) { 3729 var areaIds = mSupportedValuesChangeCallbackByPropIdAreaId.getSecondKeysForFirstKey( 3730 propertyId); 3731 if (areaIds.isEmpty()) { 3732 Slogf.d(TAG, "No SupportedValuesChangeCallback was registered for property: " 3733 + "%s, do nothing", VehiclePropertyIds.toString(propertyId)); 3734 return; 3735 } 3736 unregisterSupportedValuesChangeCallbackWithAreaIdsLocked(propertyId, areaIds, cb); 3737 } 3738 } 3739 3740 /** 3741 * Unregisters the specific callback for the [propertyId, areaId]. 3742 * 3743 * <p>Do nothing if the callback was not registered before. 3744 * 3745 * @param propertyId The property ID. 3746 * @param areaId The area ID. 3747 * @param cb The callback to unregister. 3748 * @throws IllegalArgumentException if the [propertyId, areaId] is not supported. 3749 */ 3750 @FlaggedApi(FLAG_CAR_PROPERTY_SUPPORTED_VALUE) unregisterSupportedValuesChangeCallback(int propertyId, int areaId, @NonNull SupportedValuesChangeCallback cb)3751 public void unregisterSupportedValuesChangeCallback(int propertyId, int areaId, 3752 @NonNull SupportedValuesChangeCallback cb) { 3753 synchronized (mLock) { 3754 unregisterSupportedValuesChangeCallbackWithAreaIdsLocked(propertyId, 3755 new ArraySet<>(Set.of(areaId)), cb); 3756 } 3757 } 3758 3759 @GuardedBy("mLock") unregisterSupportedValuesChangeCallbackWithAreaIdsLocked(int propertyId, ArraySet<Integer> areaIds, SupportedValuesChangeCallback cb)3760 private void unregisterSupportedValuesChangeCallbackWithAreaIdsLocked(int propertyId, 3761 ArraySet<Integer> areaIds, SupportedValuesChangeCallback cb) { 3762 List<PropIdAreaId> propIdAreaIds = new ArrayList<>(); 3763 boolean found = false; 3764 var propIdAreaIdsForCallback = mPropIdAreaIdsBySupportedValuesChangeCallback.get(cb); 3765 for (int i = 0; i < areaIds.size(); i++) { 3766 var areaId = areaIds.valueAt(i); 3767 var callbacks = mSupportedValuesChangeCallbackByPropIdAreaId.get(propertyId, 3768 areaId); 3769 if (callbacks == null || !callbacks.contains(cb)) { 3770 continue; 3771 } 3772 found = true; 3773 callbacks.remove(cb); 3774 if (callbacks.isEmpty()) { 3775 mSupportedValuesChangeCallbackByPropIdAreaId.remove(propertyId, areaId); 3776 propIdAreaIds.add(newPropIdAreaId(propertyId, areaId)); 3777 } 3778 if (propIdAreaIdsForCallback != null) { 3779 propIdAreaIdsForCallback.remove(newPropIdAreaId(propertyId, areaId)); 3780 } 3781 } 3782 if (propIdAreaIdsForCallback != null && propIdAreaIdsForCallback.isEmpty()) { 3783 // There is no more [propId, areaId]s registered for the callback, we can now unlink 3784 // the executor. 3785 mPropIdAreaIdsBySupportedValuesChangeCallback.remove(cb); 3786 mExecutorBySupportedValuesChangeCallback.remove(cb); 3787 } 3788 if (!found) { 3789 Slogf.d(TAG, "No SupportedValuesChangeCallback was registered for the callback " 3790 + "for property: %s, do nothing", VehiclePropertyIds.toString(propertyId)); 3791 return; 3792 } 3793 if (propIdAreaIds.isEmpty()) { 3794 return; 3795 } 3796 3797 // Even though this involves a binder call, we call this inside the lock so that this 3798 // whole block does not overlap with another unregister or register operation. 3799 unregisterSupportedValuesChangeCbToCarService(propIdAreaIds); 3800 } 3801 3802 private static class EventDispatchList extends 3803 DispatchList<CarPropertyEventCallbackController, CarPropertyEvent> { 3804 @Override dispatchToClient(CarPropertyEventCallbackController client, List<CarPropertyEvent> events)3805 protected void dispatchToClient(CarPropertyEventCallbackController client, 3806 List<CarPropertyEvent> events) { 3807 for (int j = 0; j < events.size(); j++) { 3808 client.onEvent(events.get(j)); 3809 } 3810 } 3811 } 3812 handleCarPropertyEvents(List<CarPropertyEvent> carPropertyEvents)3813 private void handleCarPropertyEvents(List<CarPropertyEvent> carPropertyEvents) { 3814 SparseArray<List<CarPropertyEvent>> carPropertyEventsByPropertyId = new SparseArray<>(); 3815 for (int i = 0; i < carPropertyEvents.size(); i++) { 3816 CarPropertyEvent carPropertyEvent = carPropertyEvents.get(i); 3817 int propertyId = carPropertyEvent.getCarPropertyValue().getPropertyId(); 3818 if (carPropertyEventsByPropertyId.get(propertyId) == null) { 3819 carPropertyEventsByPropertyId.put(propertyId, new ArrayList<>()); 3820 } 3821 carPropertyEventsByPropertyId.get(propertyId).add(carPropertyEvent); 3822 } 3823 3824 var eventsDispatchList = new EventDispatchList(); 3825 3826 synchronized (mLock) { 3827 for (int i = 0; i < carPropertyEventsByPropertyId.size(); i++) { 3828 int propertyId = carPropertyEventsByPropertyId.keyAt(i); 3829 List<CarPropertyEvent> eventsForPropertyId = 3830 carPropertyEventsByPropertyId.valueAt(i); 3831 ArraySet<CarPropertyEventCallbackController> cpeCallbackControllerSet = 3832 mPropIdToCpeCallbackControllerList.get(propertyId); 3833 if (cpeCallbackControllerSet == null) { 3834 Slog.w(TAG, "handleEvent: could not find any callbacks for propertyId=" 3835 + VehiclePropertyIds.toString(propertyId)); 3836 return; 3837 } 3838 for (int j = 0; j < cpeCallbackControllerSet.size(); j++) { 3839 var callback = cpeCallbackControllerSet.valueAt(j); 3840 eventsDispatchList.addEvents(callback, eventsForPropertyId); 3841 } 3842 } 3843 } 3844 3845 // This might be invoked from a binder thread (CarPropertyEventListenerToService.onEvent), 3846 // so we must clear calling identity before calling client executor. 3847 Binder.clearCallingIdentity(); 3848 eventsDispatchList.dispatchToClients(); 3849 } 3850 registerSupportedValuesChangeCallbackInternal(int propertyId, @Nullable @CallbackExecutor Executor callbackExecutor, @NonNull SupportedValuesChangeCallback cb)3851 private boolean registerSupportedValuesChangeCallbackInternal(int propertyId, 3852 @Nullable @CallbackExecutor Executor callbackExecutor, 3853 @NonNull SupportedValuesChangeCallback cb) { 3854 requireNonNull(cb); 3855 assertPropertyIdIsSupported(propertyId); 3856 3857 CarPropertyConfigs configs = getPropertyConfigsFromService( 3858 new ArraySet(Set.of(propertyId))); 3859 if (configs == null) { 3860 Slog.e(TAG, "Failed to get car property config from car service"); 3861 return false; 3862 } 3863 3864 verifyPropertyConfigForProperty(configs, propertyId); 3865 3866 CarPropertyConfig<?> config = configs.getConfig(propertyId); 3867 int[] areaIds = config.getAreaIds(); 3868 3869 return registerSupportedValuesChangeCallbackForPropIdAreaIds( 3870 propertyId, areaIds, callbackExecutor, cb); 3871 } 3872 registerSupportedValuesChangeCallbackInternal(int propertyId, int areaId, @Nullable @CallbackExecutor Executor callbackExecutor, @NonNull SupportedValuesChangeCallback cb)3873 private boolean registerSupportedValuesChangeCallbackInternal(int propertyId, int areaId, 3874 @Nullable @CallbackExecutor Executor callbackExecutor, 3875 @NonNull SupportedValuesChangeCallback cb) { 3876 requireNonNull(cb); 3877 assertPropertyIdIsSupported(propertyId); 3878 3879 return registerSupportedValuesChangeCallbackForPropIdAreaIds( 3880 propertyId, new int[]{areaId}, callbackExecutor, cb); 3881 } 3882 registerSupportedValuesChangeCallbackForPropIdAreaIds( int propertyId, int[] areaIds, @Nullable @CallbackExecutor Executor callbackExecutor, SupportedValuesChangeCallback cb)3883 private boolean registerSupportedValuesChangeCallbackForPropIdAreaIds( 3884 int propertyId, int[] areaIds, 3885 @Nullable @CallbackExecutor Executor callbackExecutor, 3886 SupportedValuesChangeCallback cb) { 3887 if (callbackExecutor == null) { 3888 callbackExecutor = mExecutor; 3889 } 3890 3891 List<PropIdAreaId> propIdAreaIds = new ArrayList<>(); 3892 for (int areaId : areaIds) { 3893 propIdAreaIds.add(newPropIdAreaId(propertyId, areaId)); 3894 } 3895 synchronized (mLock) { 3896 var associatedExecutor = mExecutorBySupportedValuesChangeCallback.get(cb); 3897 if (associatedExecutor != null && associatedExecutor != callbackExecutor) { 3898 throw new IllegalArgumentException("A different executor is already associated with" 3899 + " this callback, please use the same executor."); 3900 } 3901 3902 if (!registerSupportedValuesChangeCbToCarService(propIdAreaIds)) { 3903 return false; 3904 } 3905 3906 mExecutorBySupportedValuesChangeCallback.put(cb, callbackExecutor); 3907 var propIdAreaIdsForCallback = mPropIdAreaIdsBySupportedValuesChangeCallback.get(cb); 3908 if (propIdAreaIdsForCallback == null) { 3909 propIdAreaIdsForCallback = new ArraySet<PropIdAreaId>(); 3910 } 3911 propIdAreaIdsForCallback.addAll(propIdAreaIds); 3912 mPropIdAreaIdsBySupportedValuesChangeCallback.put(cb, propIdAreaIdsForCallback); 3913 for (int areaId : areaIds) { 3914 var registeredCallbacks = mSupportedValuesChangeCallbackByPropIdAreaId.get( 3915 propertyId, areaId); 3916 if (registeredCallbacks == null) { 3917 registeredCallbacks = new ArraySet<>(); 3918 } 3919 registeredCallbacks.add(cb); 3920 mSupportedValuesChangeCallbackByPropIdAreaId.put(propertyId, areaId, 3921 registeredCallbacks); 3922 } 3923 } 3924 return true; 3925 } 3926 registerSupportedValuesChangeCbToCarService(List<PropIdAreaId> propIdAreaIds)3927 private boolean registerSupportedValuesChangeCbToCarService(List<PropIdAreaId> propIdAreaIds) { 3928 try { 3929 mService.registerSupportedValuesChangeCallback(propIdAreaIds, 3930 mCarServiceSupportedValuesChangeCallback); 3931 } catch (RemoteException e) { 3932 Slog.e(TAG, "Failed to register SupportedValuesChangeCallback", e); 3933 return handleRemoteExceptionFromCarService(e, false); 3934 } catch (ServiceSpecificException e) { 3935 Slog.e(TAG, "Failed to register SupportedValuesChangeCallback", e); 3936 return false; 3937 } 3938 3939 return true; 3940 } 3941 unregisterSupportedValuesChangeCbToCarService(List<PropIdAreaId> propIdAreaIds)3942 private void unregisterSupportedValuesChangeCbToCarService(List<PropIdAreaId> propIdAreaIds) { 3943 try { 3944 mService.unregisterSupportedValuesChangeCallback(propIdAreaIds, 3945 mCarServiceSupportedValuesChangeCallback); 3946 } catch (RemoteException e) { 3947 Slog.e(TAG, "Failed to unregister SupportedValuesChangeCallback", e); 3948 handleRemoteExceptionFromCarService(e); 3949 } 3950 } 3951 assertPropertyIdIsSupported(int propertyId)3952 private void assertPropertyIdIsSupported(int propertyId) { 3953 if (!CarPropertyHelper.isSupported(propertyId)) { 3954 throw new IllegalArgumentException("The property: " 3955 + VehiclePropertyIds.toString(propertyId) + " is unsupported"); 3956 } 3957 } 3958 3959 private <RequestType extends AsyncPropertyRequest, CallbackType> List<Integer> storePendingRequestInfo( List<RequestType> requests, Executor callbackExecutor, CallbackType callback)3960 storePendingRequestInfo( 3961 List<RequestType> requests, Executor callbackExecutor, CallbackType callback) { 3962 if (DBG) { 3963 Slog.d(TAG, "store pending async requests: " + requests); 3964 } 3965 List<Integer> requestIds = new ArrayList<>(); 3966 SparseArray<AsyncPropertyRequestInfo<?, ?>> requestInfoToAdd = new SparseArray<>(); 3967 synchronized (mLock) { 3968 for (int i = 0; i < requests.size(); i++) { 3969 RequestType request = requests.get(i); 3970 AsyncPropertyRequestInfo<RequestType, CallbackType> requestInfo = 3971 new AsyncPropertyRequestInfo(request, callbackExecutor, callback); 3972 int requestId = request.getRequestId(); 3973 requestIds.add(requestId); 3974 if (mRequestIdToAsyncRequestInfo.contains(requestId) 3975 || requestInfoToAdd.contains(requestId)) { 3976 throw new IllegalArgumentException( 3977 "Request ID: " + requestId + " already exists"); 3978 } 3979 requestInfoToAdd.put(requestId, requestInfo); 3980 } 3981 for (int i = 0; i < requestInfoToAdd.size(); i++) { 3982 mRequestIdToAsyncRequestInfo.put(requestInfoToAdd.keyAt(i), 3983 requestInfoToAdd.valueAt(i)); 3984 } 3985 } 3986 return requestIds; 3987 } 3988 sanitizeSubscribeOptions(List<CarSubscription> subscribeOptions)3989 private List<CarSubscription> sanitizeSubscribeOptions(List<CarSubscription> subscribeOptions) 3990 throws IllegalArgumentException, IllegalStateException { 3991 ArraySet<Integer> propertyIds = new ArraySet<>(); 3992 for (int i = 0; i < subscribeOptions.size(); i++) { 3993 propertyIds.add(subscribeOptions.get(i).propertyId); 3994 } 3995 CarPropertyConfigs configs = getPropertyConfigsFromService(propertyIds); 3996 if (configs == null) { 3997 throw new IllegalStateException("Failed to get property config list from car service"); 3998 } 3999 4000 List<CarSubscription> output = new ArrayList<>(); 4001 for (int i = 0; i < subscribeOptions.size(); i++) { 4002 CarSubscription subscribeOption = subscribeOptions.get(i); 4003 int propertyId = subscribeOption.propertyId; 4004 4005 verifyPropertyConfigForProperty(configs, propertyId); 4006 4007 CarPropertyConfig<?> carPropertyConfig = configs.getConfig(propertyId); 4008 CarSubscription carSubscription = new CarSubscription(); 4009 carSubscription.propertyId = propertyId; 4010 carSubscription.areaIds = subscribeOption.areaIds; 4011 if (carSubscription.areaIds.length == 0) { 4012 // Subscribe to all areaIds if not specified. 4013 carSubscription.areaIds = carPropertyConfig.getAreaIds(); 4014 } 4015 carSubscription.enableVariableUpdateRate = 4016 subscribeOption.enableVariableUpdateRate; 4017 carSubscription.updateRateHz = InputSanitizationUtils.sanitizeUpdateRateHz( 4018 carPropertyConfig, subscribeOption.updateRateHz); 4019 float resolution = mFeatureFlags.subscriptionWithResolution() 4020 ? subscribeOption.resolution : 0.0f; 4021 carSubscription.resolution = InputSanitizationUtils.sanitizeResolution(mFeatureFlags, 4022 carPropertyConfig, resolution); 4023 output.addAll(InputSanitizationUtils.sanitizeEnableVariableUpdateRate( 4024 mFeatureFlags, carPropertyConfig, carSubscription)); 4025 } 4026 return output; 4027 } 4028 4029 /** 4030 * Checks if the given property ID is one of the user HAL property. 4031 * 4032 * <p>Properties related to user management should only be manipulated by 4033 * {@code UserHalService} and should not be used by directly by the client. 4034 * 4035 * <p>This check is no longer necessary for clients after U, but this logic exists before U so 4036 * we still need this to keep backward compatibility for clients before U. 4037 * 4038 * @param propId property to be checked 4039 * 4040 * @throws IllegalArgumentException if the property is not supported. 4041 */ assertNotUserHalProperty(int propId)4042 private void assertNotUserHalProperty(int propId) { 4043 if (mAppTargetSdk >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 4044 // After Android U, we treat this the same as other unsupported property IDs and this 4045 // special logic is no longer required. 4046 return; 4047 } 4048 switch (propId) { 4049 case VehiclePropertyIds.INITIAL_USER_INFO: 4050 case VehiclePropertyIds.SWITCH_USER: 4051 case VehiclePropertyIds.CREATE_USER: 4052 case VehiclePropertyIds.REMOVE_USER: 4053 case VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION: 4054 throw new IllegalArgumentException("Unsupported property: " 4055 + VehiclePropertyIds.toString(propId) + " (" + propId + ")"); 4056 } 4057 } 4058 filterOutUserHalProperty(List<Integer> propertyIds)4059 private List<Integer> filterOutUserHalProperty(List<Integer> propertyIds) { 4060 if (mAppTargetSdk >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 4061 // After Android U, we treat this the same as other unsupported property IDs and this 4062 // special logic is no longer required. 4063 return propertyIds; 4064 } 4065 List<Integer> filteredPropertyIds = new ArrayList<>(); 4066 for (int i = 0; i < propertyIds.size(); i++) { 4067 switch (propertyIds.get(i)) { 4068 case VehiclePropertyIds.INITIAL_USER_INFO: 4069 case VehiclePropertyIds.SWITCH_USER: 4070 case VehiclePropertyIds.CREATE_USER: 4071 case VehiclePropertyIds.REMOVE_USER: 4072 case VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION: 4073 continue; 4074 } 4075 filteredPropertyIds.add(propertyIds.get(i)); 4076 } 4077 return filteredPropertyIds; 4078 } 4079 getSupportedNoReadPermPropIds(List<CarSubscription> subscribeOptions)4080 private int[] getSupportedNoReadPermPropIds(List<CarSubscription> subscribeOptions) 4081 throws RemoteException { 4082 ArraySet<Integer> propertyIds = new ArraySet<>(); 4083 for (int i = 0; i < subscribeOptions.size(); i++) { 4084 propertyIds.add(subscribeOptions.get(i).propertyId); 4085 } 4086 int[] propertyIdsArray = new int[propertyIds.size()]; 4087 for (int i = 0; i < propertyIds.size(); i++) { 4088 propertyIdsArray[i] = propertyIds.valueAt(i); 4089 } 4090 4091 return mService.getSupportedNoReadPermPropIds(propertyIdsArray); 4092 } 4093 4094 // Wraps the result returned from {@code ICarProperty.getPropertyConfigList}. 4095 private static final class CarPropertyConfigs { 4096 private final SparseArray<CarPropertyConfig<?>> mCarPropertyConfigById = 4097 new SparseArray<>(); 4098 private final ArraySet<Integer> mMissingPermissionPropIds = new ArraySet<>(); 4099 private final ArraySet<Integer> mUnsupportedPropIds = new ArraySet<>(); 4100 private final GetPropertyConfigListResult mResult; 4101 4102 // The unsupportedPropIds are the property Ids we filtered out before we send out 4103 // the request to car service to get the configs. CarPropertyConfigs(GetPropertyConfigListResult result, IntArray unsupportedPropIds)4104 CarPropertyConfigs(GetPropertyConfigListResult result, IntArray unsupportedPropIds) { 4105 mResult = result; 4106 List<CarPropertyConfig> configs = result.carPropertyConfigList.getConfigs(); 4107 for (int i = 0; i < configs.size(); i++) { 4108 mCarPropertyConfigById.put(configs.get(i).getPropertyId(), configs.get(i)); 4109 } 4110 for (int i = 0; i < result.missingPermissionPropIds.length; i++) { 4111 mMissingPermissionPropIds.add(result.missingPermissionPropIds[i]); 4112 } 4113 for (int i = 0; i < result.unsupportedPropIds.length; i++) { 4114 mUnsupportedPropIds.add(result.unsupportedPropIds[i]); 4115 } 4116 for (int i = 0; i < unsupportedPropIds.size(); i++) { 4117 mUnsupportedPropIds.add(unsupportedPropIds.get(i)); 4118 } 4119 } 4120 4121 // For the propertyIds provided to {@code getPropertyConfigsFromService}, this must not 4122 // return null if both {@code isNotSupported} and {@code missingPermission} is false. 4123 @Nullable getConfig(int propertyId)4124 CarPropertyConfig<?> getConfig(int propertyId) { 4125 return mCarPropertyConfigById.get(propertyId); 4126 } 4127 4128 // Returns whether the property is not supported. isNotSupported(int propertyId)4129 boolean isNotSupported(int propertyId) { 4130 return mUnsupportedPropIds.contains(propertyId); 4131 } 4132 4133 // Returns whether the caller does not have read and does not have write access to this 4134 // property, hence the caller cannot get the property's config. missingPermission(int propertyId)4135 boolean missingPermission(int propertyId) { 4136 return mMissingPermissionPropIds.contains(propertyId); 4137 } 4138 getConfigs()4139 List<CarPropertyConfig> getConfigs() { 4140 return mResult.carPropertyConfigList.getConfigs(); 4141 } 4142 getMissingPermissionPropIds()4143 int[] getMissingPermissionPropIds() { 4144 return mResult.missingPermissionPropIds; 4145 } 4146 getUnsupportedPropIds()4147 int[] getUnsupportedPropIds() { 4148 return mResult.unsupportedPropIds; 4149 } 4150 } 4151 4152 @Nullable getPropertyConfigsFromService(Iterable<Integer> propertyIds)4153 private CarPropertyConfigs getPropertyConfigsFromService(Iterable<Integer> propertyIds) { 4154 IntArray filteredPropertyIds = new IntArray(); 4155 IntArray unsupportedPropertyIds = new IntArray(); 4156 for (int propertyId : propertyIds) { 4157 assertNotUserHalProperty(propertyId); 4158 if (!CarPropertyHelper.isSupported(propertyId)) { 4159 unsupportedPropertyIds.add(propertyId); 4160 continue; 4161 } 4162 filteredPropertyIds.add(propertyId); 4163 } 4164 GetPropertyConfigListResult result; 4165 try { 4166 result = mService.getPropertyConfigList(filteredPropertyIds.toArray()); 4167 } catch (RemoteException e) { 4168 Slog.e(TAG, "CarPropertyService.getPropertyConfigList exception ", e); 4169 return handleRemoteExceptionFromCarService(e, null); 4170 } 4171 return new CarPropertyConfigs(result, unsupportedPropertyIds); 4172 } 4173 4174 @Nullable extractRawPropertyValue( ParcelableHolder holder)4175 private static RawPropertyValue<?> extractRawPropertyValue( 4176 ParcelableHolder holder) { 4177 return holder.getParcelable(RawPropertyValue.class); 4178 } 4179 verifyPropertyConfigForProperty(CarPropertyConfigs configs, int propertyId)4180 private static void verifyPropertyConfigForProperty(CarPropertyConfigs configs, 4181 int propertyId) { 4182 if (configs.isNotSupported(propertyId)) { 4183 String errorMessage = "propertyId is not in carPropertyConfig list: " 4184 + VehiclePropertyIds.toString(propertyId); 4185 Slog.e(TAG, "verifyPropertyConfigForProperty: " + errorMessage); 4186 throw new IllegalArgumentException(errorMessage); 4187 } 4188 if (configs.missingPermission(propertyId)) { 4189 String errorMessage = "missing required read/write permission for: " 4190 + VehiclePropertyIds.toString(propertyId); 4191 Slog.e(TAG, "verifyPropertyConfigForProperty: " + errorMessage); 4192 throw new SecurityException(errorMessage); 4193 } 4194 } 4195 4196 } 4197