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