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.CarPropertyHelper.STATUS_OK; 21 import static com.android.car.internal.property.CarPropertyHelper.SYNC_OP_LIMIT_TRY_AGAIN; 22 import static com.android.car.internal.util.VersionUtils.assertPlatformVersionAtLeastU; 23 24 import static java.lang.Integer.toHexString; 25 import static java.util.Objects.requireNonNull; 26 27 import android.annotation.FloatRange; 28 import android.annotation.IntDef; 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.annotation.SuppressLint; 32 import android.annotation.SystemApi; 33 import android.car.Car; 34 import android.car.CarManagerBase; 35 import android.car.VehiclePropertyIds; 36 import android.car.annotation.AddedInOrBefore; 37 import android.car.annotation.ApiRequirements; 38 import android.car.hardware.CarPropertyConfig; 39 import android.car.hardware.CarPropertyValue; 40 import android.os.Build; 41 import android.os.CancellationSignal; 42 import android.os.Handler; 43 import android.os.IBinder; 44 import android.os.RemoteException; 45 import android.os.ServiceSpecificException; 46 import android.os.SystemClock; 47 import android.os.Trace; 48 import android.util.ArraySet; 49 import android.util.Log; 50 import android.util.SparseArray; 51 52 import com.android.car.internal.CarPropertyEventCallbackController; 53 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 54 import com.android.car.internal.SingleMessageHandler; 55 import com.android.car.internal.os.HandlerExecutor; 56 import com.android.car.internal.property.AsyncPropertyServiceRequest; 57 import com.android.car.internal.property.AsyncPropertyServiceRequestList; 58 import com.android.car.internal.property.CarPropertyHelper; 59 import com.android.car.internal.property.GetSetValueResult; 60 import com.android.car.internal.property.GetSetValueResultList; 61 import com.android.car.internal.property.IAsyncPropertyResultCallback; 62 import com.android.car.internal.property.InputSanitizationUtils; 63 import com.android.internal.annotations.GuardedBy; 64 65 import java.lang.annotation.Retention; 66 import java.lang.annotation.RetentionPolicy; 67 import java.lang.ref.WeakReference; 68 import java.util.ArrayList; 69 import java.util.List; 70 import java.util.concurrent.Executor; 71 import java.util.concurrent.atomic.AtomicInteger; 72 73 /** 74 * Provides an application interface for interacting with the Vehicle specific properties. 75 * For details about the individual properties, see the descriptions in {@link VehiclePropertyIds} 76 */ 77 public class CarPropertyManager extends CarManagerBase { 78 private static final String TAG = "CarPropertyManager"; 79 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 80 private static final int MSG_GENERIC_EVENT = 0; 81 private static final int SYNC_OP_RETRY_SLEEP_IN_MS = 10; 82 private static final int SYNC_OP_RETRY_MAX_COUNT = 10; 83 /** 84 * The default timeout in MS for {@link CarPropertyManager#getPropertiesAsync}. 85 */ 86 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 87 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 88 public static final long ASYNC_GET_DEFAULT_TIMEOUT_MS = 10_000; 89 90 private final SingleMessageHandler<CarPropertyEvent> mHandler; 91 private final ICarProperty mService; 92 private final int mAppTargetSdk; 93 private final AtomicInteger mRequestIdCounter = new AtomicInteger(0); 94 @GuardedBy("mLock") 95 private final SparseArray<AsyncPropertyRequestInfo<?, ?>> mRequestIdToAsyncRequestInfo = 96 new SparseArray<>(); 97 private final AsyncPropertyResultCallback mAsyncPropertyResultCallback = 98 new AsyncPropertyResultCallback(); 99 100 private final CarPropertyEventListenerToService mCarPropertyEventToService = 101 new CarPropertyEventListenerToService(this); 102 103 // This lock is shared with all CarPropertyEventCallbackController instances to prevent 104 // potential deadlock. 105 private final Object mLock = new Object(); 106 @GuardedBy("mLock") 107 private final SparseArray<CarPropertyEventCallbackController> 108 mPropertyIdToCarPropertyEventCallbackController = new SparseArray<>(); 109 110 private final CarPropertyEventCallbackController.RegistrationUpdateCallback 111 mRegistrationUpdateCallback = 112 new CarPropertyEventCallbackController.RegistrationUpdateCallback() { 113 @Override 114 public boolean register(int propertyId, float updateRateHz) { 115 try { 116 mService.registerListener(propertyId, updateRateHz, 117 mCarPropertyEventToService); 118 } catch (RemoteException e) { 119 handleRemoteExceptionFromCarService(e); 120 return false; 121 } catch (IllegalArgumentException e) { 122 Log.w(TAG, "register: propertyId: " 123 + VehiclePropertyIds.toString(propertyId) + ", updateRateHz: " 124 + updateRateHz + ", exception: ", e); 125 return false; 126 } 127 return true; 128 } 129 130 @Override 131 public boolean unregister(int propertyId) { 132 try { 133 mService.unregisterListener(propertyId, mCarPropertyEventToService); 134 } catch (RemoteException e) { 135 handleRemoteExceptionFromCarService(e); 136 return false; 137 } catch (IllegalArgumentException e) { 138 Log.w(TAG, "unregister: propertyId: " 139 + VehiclePropertyIds.toString(propertyId) + ", exception: ", e); 140 return false; 141 } 142 return true; 143 } 144 }; 145 146 private final GetPropertyResultCallback mGetPropertyResultCallback = 147 new GetPropertyResultCallback(); 148 149 private final SetPropertyResultCallback mSetPropertyResultCallback = 150 new SetPropertyResultCallback(); 151 152 /** 153 * Application registers {@link CarPropertyEventCallback} object to receive updates and changes 154 * to subscribed Vehicle specific properties. 155 */ 156 public interface CarPropertyEventCallback { 157 /** 158 * Called when a property is updated 159 * 160 * @param value the new value of the property 161 */ 162 @AddedInOrBefore(majorVersion = 33) onChangeEvent(CarPropertyValue value)163 void onChangeEvent(CarPropertyValue value); 164 165 /** 166 * Called when an error is detected when setting a property. 167 * 168 * @param propertyId the property ID which has detected an error 169 * @param areaId the area ID which has detected an error 170 * 171 * @see CarPropertyEventCallback#onErrorEvent(int, int, int) 172 */ 173 @AddedInOrBefore(majorVersion = 33) onErrorEvent(int propertyId, int areaId)174 void onErrorEvent(int propertyId, int areaId); 175 176 /** 177 * Called when an error is detected when setting a property. 178 * 179 * <p>Clients which changed the property value in the areaId most recently will receive 180 * this callback. If multiple clients set a property for the same area ID simultaneously, 181 * which one takes precedence is undefined. Typically, the last set operation 182 * (in the order that they are issued to car's ECU) overrides the previous set operations. 183 * The delivered error reflects the error happened in the last set operation. 184 * 185 * @param propertyId the property ID which is detected an error 186 * @param areaId the area ID which is detected an error 187 * @param errorCode the error code is raised in the car 188 */ 189 @AddedInOrBefore(majorVersion = 33) onErrorEvent(int propertyId, int areaId, @CarSetPropertyErrorCode int errorCode)190 default void onErrorEvent(int propertyId, int areaId, 191 @CarSetPropertyErrorCode int errorCode) { 192 if (DBG) { 193 Log.d(TAG, "onErrorEvent propertyId: " + VehiclePropertyIds.toString(propertyId) 194 + " areaId: 0x" + toHexString(areaId) + " ErrorCode: " + errorCode); 195 } 196 onErrorEvent(propertyId, areaId); 197 } 198 } 199 200 /** 201 * A callback {@link CarPropertyManager#getPropertiesAsync} when succeeded or failed. 202 */ 203 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 204 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 205 public interface GetPropertyCallback { 206 /** 207 * Method called when {@link GetPropertyRequest} successfully gets a result. 208 */ 209 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 210 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) onSuccess(@onNull GetPropertyResult<?> getPropertyResult)211 void onSuccess(@NonNull GetPropertyResult<?> getPropertyResult); 212 213 /** 214 * Method called when {@link GetPropertyRequest} returns an error. 215 */ 216 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 217 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) onFailure(@onNull PropertyAsyncError propertyAsyncError)218 void onFailure(@NonNull PropertyAsyncError propertyAsyncError); 219 } 220 221 /** 222 * A callback {@link CarPropertyManager#setPropertiesAsync} when succeeded or failed. 223 */ 224 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 225 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 226 public interface SetPropertyCallback { 227 /** 228 * Method called when the {@link SetPropertyRequest} successfully set the value. 229 * 230 * <p>This means: the set value request is successfully sent to vehicle 231 * 232 * <p>and 233 * 234 * <p>either the current property value is already the target value, or we have received a 235 * property update event indicating the value is updated to the target value. 236 * 237 * <p>If multiple clients set a property for the same area ID simultaneously with different 238 * values, the order is undefined. One possible case is that both requests are sent to the 239 * vehicle bus, which causes two property update events. As a result, the success callback 240 * would be called for both clients, but in an undefined order. This means that even if 241 * the success callback is called, it doesn't necessarily mean getting the property would 242 * return the same value you just set. Another client might have changed the value after you 243 * set it. 244 * 245 * <p>If only one requests is successfully processed by the vehicle bus, overwriting the 246 * other request, then only one success callback would be called for one client. The other 247 * client would get the failure callback with 248 * {@link CarPropertyManager#STATUS_ERROR_TIMEOUT} error code. 249 * 250 * <p>If multiple clients set a property for the same area ID simultaneously with the same 251 * value. The success callback for both clients would be called in an undefined order. 252 */ 253 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 254 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) onSuccess(@onNull SetPropertyResult setPropertyResult)255 void onSuccess(@NonNull SetPropertyResult setPropertyResult); 256 257 /** 258 * Method called when {@link SetPropertyRequest} returns an error. 259 */ 260 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 261 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) onFailure(@onNull PropertyAsyncError propertyAsyncError)262 void onFailure(@NonNull PropertyAsyncError propertyAsyncError); 263 } 264 265 /** 266 * An async get/set property request. 267 */ 268 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 269 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 270 public interface AsyncPropertyRequest { 271 /** 272 * Returns the unique ID for this request. 273 * 274 * <p>Each request must have a unique request ID so the responses can be differentiated. 275 */ 276 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 277 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getRequestId()278 int getRequestId(); 279 280 /** 281 * Returns the ID for the property of this request. 282 * 283 * <p>The ID must be one of the {@link VehiclePropertyIds} or vendor property IDs. 284 */ 285 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 286 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getPropertyId()287 int getPropertyId(); 288 289 /** 290 * Returns the area ID for the property of this request. 291 */ 292 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 293 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getAreaId()294 int getAreaId(); 295 } 296 297 /** 298 * A request for {@link CarPropertyManager#getPropertiesAsync(List, long, CancellationSignal, 299 * Executor, GetPropertyCallback)}. 300 */ 301 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 302 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 303 public static final class GetPropertyRequest implements AsyncPropertyRequest { 304 private final int mRequestId; 305 private final int mPropertyId; 306 private final int mAreaId; 307 308 /** 309 * @see AsyncPropertyRequest#getRequestId 310 */ 311 @Override 312 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 313 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getRequestId()314 public int getRequestId() { 315 return mRequestId; 316 } 317 318 /** 319 * @see AsyncPropertyRequest#getPropertyId 320 */ 321 @Override 322 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 323 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getPropertyId()324 public int getPropertyId() { 325 return mPropertyId; 326 } 327 328 /** 329 * @see AsyncPropertyRequest#getAreaId 330 */ 331 @Override 332 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 333 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getAreaId()334 public int getAreaId() { 335 return mAreaId; 336 } 337 338 /** 339 * Internal use only. Users should use {@link #generateGetPropertyRequest(int, int)} 340 * instead. 341 */ GetPropertyRequest(int requestId, int propertyId, int areaId)342 private GetPropertyRequest(int requestId, int propertyId, int areaId) { 343 mRequestId = requestId; 344 mPropertyId = propertyId; 345 mAreaId = areaId; 346 } 347 348 /** 349 * Prints out debug message. 350 */ 351 @Override 352 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) toString()353 public String toString() { 354 return new StringBuilder() 355 .append("GetPropertyRequest{request ID: ") 356 .append(mRequestId) 357 .append(", property ID: ") 358 .append(VehiclePropertyIds.toString(mPropertyId)) 359 .append(", area ID: ") 360 .append(mAreaId) 361 .append("}").toString(); 362 } 363 } 364 365 /** 366 * A request for {@link CarPropertyManager#setPropertiesAsync(List, long, CancellationSignal, 367 * Executor, SetPropertyCallback)}. 368 * 369 * @param <T> the type for the property value, must be one of Object, Boolean, Float, Integer, 370 * Long, Float[], Integer[], Long[], String, byte[], Object[] 371 */ 372 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 373 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 374 public static final class SetPropertyRequest<T> implements AsyncPropertyRequest { 375 private final int mRequestId; 376 private final int mPropertyId; 377 private final int mAreaId; 378 private float mUpdateRateHz = 0.f; 379 // By default, the async set operation will wait for the property to be updated to the 380 // target value before calling the success callback (or when the target value is the 381 // same as the current value). 382 private boolean mWaitForPropertyUpdate = true; 383 384 /** 385 * The value to set. 386 */ 387 private final T mValue; 388 389 /** 390 * Sets the update rate in Hz for listening for property updates for continuous property. 391 * 392 * <p>If {@code waitForPropertyUpdate} is set to {@code true} (by default) and if the 393 * property is set to a different value than its current value, the success callback will be 394 * called when a property update event for the new value arrived. This option controls how 395 * frequent the property update event should be reported for continuous property. This is 396 * similar to {@code updateRateHz} in {@link CarPropertyManager#registerCallback}. 397 * 398 * <p>This is ignored for non-continuous properties. 399 * 400 * <p>This is ignored if {@code waitForPropertyUpdate} is set to {@code false}. 401 */ 402 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 403 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setUpdateRateHz(float updateRateHz)404 public void setUpdateRateHz(float updateRateHz) { 405 mUpdateRateHz = updateRateHz; 406 } 407 408 /** 409 * Sets whether to wait for the property update event before calling success callback. 410 * 411 * <p>This arguments controls under what conditions the operation is considered succeeded 412 * and the success callback will be called. 413 * 414 * <p>If this is set to {@code true} (by default), the success callback will be called when 415 * both of the following coniditions are met: 416 * 417 * <ul> 418 * <li>the set operation is successfully delivered to vehicle bus. 419 * <li>the {@code mPropertyId}+{@code mAreaId}'s value already equal to {@code mValue} or 420 * is successfully updated to the {@code mValue} through the set operation. 421 * </ul> 422 * 423 * <p>Even if the target value is the same as the current value, we will still send the set 424 * operation to the vehicle bus. If caller wants to reduce unnecessary overhead, caller must 425 * check existing values before issuing the requests. 426 * 427 * <p>If the first condition fails, the error callback will be called. If the second 428 * condition fails, which means we don't see the property updated to the target value within 429 * a specified timeout, the error callback will be called with {@link 430 * #STATUS_ERROR_TIMEOUT}. 431 * 432 * <p>If this is set to {@code false}, the success callback will be called after the 433 * set operation is successfully delivered to vehicle bus. 434 * 435 * <p>Under most cases, client should wait for the property update to verify that the set 436 * operation actually succeeded. 437 * 438 * <p>For cases when the property is write-only (no way to get property update event) or 439 * when the property represents some action, instead of an actual state, e.g. key stroke 440 * where the property's current value is not meaningful, caller should set 441 * {@code waitForPropertyUpdate} to {@code false}. 442 * 443 * <p>For {@code HVAC_TEMPERATURE_VALUE_SUGGESTION}, this must be set to {@code false} 444 * because the updated property value will not be the same as the value to be set. 445 * 446 * <p>Note that even if this is set to {@code true}, it is only guaranteed that the property 447 * value is the target value after the success callback is called if no other clients are 448 * changing the property at the same time. It is always possible that another client changes 449 * the property value after the property is updated to the target value, but before the 450 * client success callback runs. We only guarantee that at some point during the period 451 * after the client issues the request and before the success callback is called, the 452 * property value was set to the target value. 453 */ 454 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 455 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setWaitForPropertyUpdate(boolean waitForPropertyUpdate)456 public void setWaitForPropertyUpdate(boolean waitForPropertyUpdate) { 457 mWaitForPropertyUpdate = waitForPropertyUpdate; 458 } 459 460 /** 461 * @see AsyncPropertyRequest#getRequestId 462 */ 463 @Override 464 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 465 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getRequestId()466 public int getRequestId() { 467 return mRequestId; 468 } 469 470 /** 471 * @see AsyncPropertyRequest#getPropertyId 472 */ 473 @Override 474 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 475 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getPropertyId()476 public int getPropertyId() { 477 return mPropertyId; 478 } 479 480 /** 481 * @see AsyncPropertyRequest#getAreaId 482 */ 483 @Override 484 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 485 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getAreaId()486 public int getAreaId() { 487 return mAreaId; 488 } 489 490 /** 491 * Get the property value to set. 492 */ 493 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 494 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getValue()495 public T getValue() { 496 return mValue; 497 } 498 499 /** 500 * Gets the update rate for listening for property updates. 501 */ 502 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 503 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getUpdateRateHz()504 public float getUpdateRateHz() { 505 return mUpdateRateHz; 506 } 507 508 /** 509 * Gets whether to wait for property update event before calling success callback. 510 */ 511 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 512 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) isWaitForPropertyUpdate()513 public boolean isWaitForPropertyUpdate() { 514 return mWaitForPropertyUpdate; 515 } 516 517 /** 518 * Internal use only. Users should use {@link #generateSetPropertyRequest(int, int, T)} 519 * instead. 520 */ SetPropertyRequest(int requestId, int propertyId, int areaId, T value)521 private SetPropertyRequest(int requestId, int propertyId, int areaId, T value) { 522 mRequestId = requestId; 523 mPropertyId = propertyId; 524 mAreaId = areaId; 525 mValue = value; 526 } 527 528 /** 529 * Prints out debug message. 530 */ 531 @Override 532 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) toString()533 public String toString() { 534 return new StringBuilder() 535 .append("SetPropertyRequest{request ID: ") 536 .append(mRequestId) 537 .append(", property ID: ") 538 .append(VehiclePropertyIds.toString(mPropertyId)) 539 .append(", area ID: ") 540 .append(mAreaId) 541 .append(", value: ") 542 .append(mValue) 543 .append(", waitForPropertyUpdate: ") 544 .append(mWaitForPropertyUpdate) 545 .append(", mUpdateRateHz: ") 546 .append(mUpdateRateHz) 547 .append("}").toString(); 548 } 549 } 550 551 /** 552 * An error result for {@link GetPropertyCallback} or {@link SetPropertyCallback}. 553 */ 554 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 555 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 556 public static final class PropertyAsyncError { 557 private final int mRequestId; 558 private final int mPropertyId; 559 private final int mAreaId; 560 private final @CarPropertyAsyncErrorCode int mErrorCode; 561 private final int mVendorErrorCode; 562 563 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 564 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getRequestId()565 public int getRequestId() { 566 return mRequestId; 567 } 568 569 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 570 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getPropertyId()571 public int getPropertyId() { 572 return mPropertyId; 573 } 574 575 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 576 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getAreaId()577 public int getAreaId() { 578 return mAreaId; 579 } 580 581 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 582 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getErrorCode()583 public @CarPropertyAsyncErrorCode int getErrorCode() { 584 return mErrorCode; 585 } 586 587 /** 588 * Gets the vendor error codes to allow for more detailed error codes. 589 * 590 * @return the vendor error code if it is set, otherwise 0. A vendor error code will have a 591 * range from 0x0000 to 0xffff. 592 * 593 * @hide 594 */ 595 @SystemApi 596 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 597 minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) getVendorErrorCode()598 public int getVendorErrorCode() { 599 assertPlatformVersionAtLeastU(); 600 return mVendorErrorCode; 601 } 602 603 /** 604 * Creates a new error result for async property request. 605 * 606 * @param requestId the request ID 607 * @param propertyId the property ID in the request 608 * @param areaId the area ID for the property in the request 609 * @param errorCode the code indicating the error 610 */ PropertyAsyncError(int requestId, int propertyId, int areaId, @CarPropertyAsyncErrorCode int errorCode, int vendorErrorCode)611 PropertyAsyncError(int requestId, int propertyId, int areaId, 612 @CarPropertyAsyncErrorCode int errorCode, 613 int vendorErrorCode) { 614 mRequestId = requestId; 615 mPropertyId = propertyId; 616 mAreaId = areaId; 617 mErrorCode = errorCode; 618 mVendorErrorCode = vendorErrorCode; 619 } 620 621 /** 622 * Prints out debug message. 623 */ 624 @Override 625 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) toString()626 public String toString() { 627 return new StringBuilder() 628 .append("PropertyAsyncError{request ID: ") 629 .append(mRequestId) 630 .append(", property: ") 631 .append(VehiclePropertyIds.toString(mPropertyId)) 632 .append(", areaId: ") 633 .append(mAreaId) 634 .append(", error code: ") 635 .append(mErrorCode) 636 .append(", vendor error code: ") 637 .append(mVendorErrorCode) 638 .append("}").toString(); 639 } 640 } 641 642 /** 643 * A successful result for {@link GetPropertyCallback}. 644 * 645 * @param <T> the type for the property value, must be one of Object, Boolean, Float, Integer, 646 * Long, Float[], Integer[], Long[], String, byte[], Object[] 647 */ 648 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 649 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 650 public static final class GetPropertyResult<T> { 651 private final int mRequestId; 652 private final int mPropertyId; 653 private final int mAreaId; 654 private final long mTimestampNanos; 655 private final T mValue; 656 657 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 658 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getRequestId()659 public int getRequestId() { 660 return mRequestId; 661 } 662 663 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 664 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getPropertyId()665 public int getPropertyId() { 666 return mPropertyId; 667 } 668 669 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 670 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getAreaId()671 public int getAreaId() { 672 return mAreaId; 673 } 674 675 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 676 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 677 @NonNull getValue()678 public T getValue() { 679 return mValue; 680 } 681 682 /** 683 * Returns the timestamp in nanoseconds at which the value for the vehicle property 684 * happened. For a given vehicle property, each new timestamp should be monotonically 685 * increasing using the same time base as {@link SystemClock#elapsedRealtimeNanos()}. 686 * 687 * <p>NOTE: Timestamp should be synchronized with other signals from the platform (e.g. 688 * {@link android.location.Location} and {@link android.hardware.SensorEvent} instances). 689 * Ideally, timestamp synchronization error should be below 1 millisecond. 690 */ 691 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 692 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getTimestampNanos()693 public long getTimestampNanos() { 694 return mTimestampNanos; 695 } 696 697 /** 698 * Creates a new value result for async GetProperty request. 699 * 700 * @param requestId the request ID 701 * @param propertyId the property ID in the request 702 * @param areaId the area ID for the property in the request 703 * @param timestampNanos the timestamp in nanoseconds when this property is updated 704 * @param value the property's value 705 */ GetPropertyResult(int requestId, int propertyId, int areaId, long timestampNanos, @NonNull T value)706 GetPropertyResult(int requestId, int propertyId, int areaId, long timestampNanos, 707 @NonNull T value) { 708 mRequestId = requestId; 709 mPropertyId = propertyId; 710 mAreaId = areaId; 711 mTimestampNanos = timestampNanos; 712 mValue = value; 713 } 714 715 /** 716 * Prints out debug message. 717 */ 718 @Override 719 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) toString()720 public String toString() { 721 return new StringBuilder() 722 .append("GetPropertyResult{type: ") 723 .append(mValue.getClass()) 724 .append(", request ID: ") 725 .append(mRequestId) 726 .append(", property: ") 727 .append(VehiclePropertyIds.toString(mPropertyId)) 728 .append(", areaId: ") 729 .append(mAreaId) 730 .append(", value: ") 731 .append(mValue) 732 .append(", timestamp: ") 733 .append(mTimestampNanos).append("ns") 734 .append("}").toString(); 735 } 736 } 737 738 /** 739 * A successful result for {@link SetPropertyCallback}. 740 */ 741 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 742 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 743 public static final class SetPropertyResult { 744 private final int mRequestId; 745 private final int mPropertyId; 746 private final int mAreaId; 747 private final long mUpdateTimestampNanos; 748 749 /** 750 * Gets the ID for the request this result is for. 751 */ 752 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 753 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getRequestId()754 public int getRequestId() { 755 return mRequestId; 756 } 757 758 /** 759 * Gets the property ID this result is for. 760 */ 761 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 762 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getPropertyId()763 public int getPropertyId() { 764 return mPropertyId; 765 } 766 767 /** 768 * Gets the area ID this result is for. 769 */ 770 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 771 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getAreaId()772 public int getAreaId() { 773 return mAreaId; 774 } 775 776 /** 777 * Gets the timestamp in nanoseconds at which the property was updated to the desired value. 778 * 779 * <p>The timestamp will use the same time base as 780 * {@link SystemClock#elapsedRealtimeNanos()}. 781 * 782 * <p>NOTE: If {@code waitForPropertyUpdate} is set to {@code false} for the request, then 783 * this value will be the time when the async set request is successfully sent to the 784 * vehicle bus, not when the property is updated since we have no way of knowing that. 785 * 786 * <p>NOTE: Timestamp should be synchronized with other signals from the platform (e.g. 787 * {@link android.location.Location} and {@link android.hardware.SensorEvent} instances). 788 * Ideally, timestamp synchronization error should be below 1 millisecond. 789 */ 790 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 791 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getUpdateTimestampNanos()792 public long getUpdateTimestampNanos() { 793 return mUpdateTimestampNanos; 794 } 795 SetPropertyResult(int requestId, int propertyId, int areaId, long updateTimestampNanos)796 SetPropertyResult(int requestId, int propertyId, int areaId, long updateTimestampNanos) { 797 mRequestId = requestId; 798 mPropertyId = propertyId; 799 mAreaId = areaId; 800 mUpdateTimestampNanos = updateTimestampNanos; 801 } 802 803 /** 804 * Prints out debug message. 805 */ 806 @Override 807 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) toString()808 public String toString() { 809 return new StringBuilder() 810 .append("SetPropertyResult{request ID: ") 811 .append(mRequestId) 812 .append(", property: ") 813 .append(VehiclePropertyIds.toString(mPropertyId)) 814 .append(", areaId: ") 815 .append(mAreaId) 816 .append(", updated timestamp: ") 817 .append(mUpdateTimestampNanos).append("ns") 818 .append("}").toString(); 819 } 820 } 821 822 /** 823 * An abstract interface for converting async get/set result and calling client callbacks. 824 */ 825 private interface PropertyResultCallback<CallbackType, ResultType> { build(int requestId, int propertyId, int areaId, long timestampNanos, @Nullable Object value)826 ResultType build(int requestId, int propertyId, int areaId, long timestampNanos, 827 @Nullable Object value); onSuccess(CallbackType callback, ResultType result)828 void onSuccess(CallbackType callback, ResultType result); onFailure(CallbackType callback, PropertyAsyncError error)829 void onFailure(CallbackType callback, PropertyAsyncError error); 830 } 831 832 /** 833 * Class to hide implementation detail for get/set callbacks. 834 */ 835 private static final class GetPropertyResultCallback implements 836 PropertyResultCallback<GetPropertyCallback, GetPropertyResult> { build(int requestId, int propertyId, int areaId, long timestampNanos, @Nullable Object value)837 public GetPropertyResult build(int requestId, int propertyId, int areaId, 838 long timestampNanos, @Nullable Object value) { 839 return new GetPropertyResult(requestId, propertyId, areaId, timestampNanos, value); 840 } 841 onSuccess(GetPropertyCallback callback, GetPropertyResult result)842 public void onSuccess(GetPropertyCallback callback, GetPropertyResult result) { 843 if (DBG) { 844 Log.d(TAG, "delivering success get property result: " + result); 845 } 846 callback.onSuccess(result); 847 } 848 onFailure(GetPropertyCallback callback, PropertyAsyncError error)849 public void onFailure(GetPropertyCallback callback, PropertyAsyncError error) { 850 if (DBG) { 851 Log.d(TAG, "delivering error get property result: " + error); 852 } 853 callback.onFailure(error); 854 } 855 } 856 857 /** 858 * Class to hide implementation detail for get/set callbacks. 859 */ 860 private static final class SetPropertyResultCallback implements 861 PropertyResultCallback<SetPropertyCallback, SetPropertyResult> { build(int requestId, int propertyId, int areaId, long timestampNanos, @Nullable Object value)862 public SetPropertyResult build(int requestId, int propertyId, int areaId, 863 long timestampNanos, @Nullable Object value) { 864 return new SetPropertyResult(requestId, propertyId, areaId, timestampNanos); 865 } 866 onSuccess(SetPropertyCallback callback, SetPropertyResult result)867 public void onSuccess(SetPropertyCallback callback, SetPropertyResult result) { 868 if (DBG) { 869 Log.d(TAG, "delivering success set property result: " + result); 870 } 871 callback.onSuccess(result); 872 } 873 onFailure(SetPropertyCallback callback, PropertyAsyncError error)874 public void onFailure(SetPropertyCallback callback, PropertyAsyncError error) { 875 if (DBG) { 876 Log.d(TAG, "delivering error set property result: " + error); 877 } 878 callback.onFailure(error); 879 } 880 } 881 882 /** 883 * A class for delivering {@link GetPropertyCallback} or {@link SetPropertyCallback} client 884 * callback when {@code IAsyncPropertyResultCallback} returns results. 885 */ 886 private class AsyncPropertyResultCallback extends IAsyncPropertyResultCallback.Stub { 887 888 @Override asBinder()889 public IBinder asBinder() { 890 return this; 891 } 892 893 @Override onGetValueResults(GetSetValueResultList getValueResults)894 public void onGetValueResults(GetSetValueResultList getValueResults) { 895 this.<GetPropertyRequest, GetPropertyCallback, GetPropertyResult>onResults( 896 getValueResults.getList(), mGetPropertyResultCallback); 897 } 898 899 @Override onSetValueResults(GetSetValueResultList setValueResults)900 public void onSetValueResults(GetSetValueResultList setValueResults) { 901 this.<SetPropertyRequest<?>, SetPropertyCallback, SetPropertyResult>onResults( 902 setValueResults.getList(), mSetPropertyResultCallback); 903 } 904 905 @SuppressLint("WrongConstant") onResults( List<GetSetValueResult> results, PropertyResultCallback<CallbackType, ResultType> propertyResultCallback)906 private <RequestType extends AsyncPropertyRequest, CallbackType, ResultType> void onResults( 907 List<GetSetValueResult> results, 908 PropertyResultCallback<CallbackType, ResultType> propertyResultCallback) { 909 for (int i = 0; i < results.size(); i++) { 910 GetSetValueResult result = results.get(i); 911 int requestId = result.getRequestId(); 912 AsyncPropertyRequestInfo<RequestType, CallbackType> requestInfo; 913 synchronized (mLock) { 914 requestInfo = 915 (AsyncPropertyRequestInfo<RequestType, CallbackType>) 916 mRequestIdToAsyncRequestInfo.get(requestId); 917 mRequestIdToAsyncRequestInfo.remove(requestId); 918 } 919 if (requestInfo == null) { 920 Log.w(TAG, "onResults: Request ID: " + requestId 921 + " might have been completed, cancelled or an exception might have " 922 + "been thrown"); 923 continue; 924 } 925 Executor callbackExecutor = requestInfo.getCallbackExecutor(); 926 CallbackType clientCallback = requestInfo.getCallback(); 927 @CarPropertyAsyncErrorCode int errorCode = result.getErrorCode(); 928 int propertyId = requestInfo.getRequest().getPropertyId(); 929 String propertyName = VehiclePropertyIds.toString(propertyId); 930 int areaId = requestInfo.getRequest().getAreaId(); 931 if (errorCode == STATUS_OK) { 932 CarPropertyValue<?> carPropertyValue = result.getCarPropertyValue(); 933 long timestampNanos; 934 if (carPropertyValue != null) { 935 // This is a get result. 936 int valuePropertyId = carPropertyValue.getPropertyId(); 937 if (propertyId != valuePropertyId) { 938 Log.e(TAG, "onResults: Request ID: " + requestId + " received get " 939 + "property value result, but has mismatch property ID, " 940 + " expect: " + propertyName + ", got: " 941 + VehiclePropertyIds.toString(valuePropertyId)); 942 } 943 int valueAreaId = carPropertyValue.getAreaId(); 944 if (areaId != valueAreaId) { 945 Log.e(TAG, "onResults: Property: " + propertyName + " Request ID: " 946 + requestId + " received get property value result, but has " 947 + "mismatch area ID, expect: " + areaId + ", got: " 948 + valueAreaId); 949 } 950 timestampNanos = carPropertyValue.getTimestamp(); 951 } else { 952 // This is a set result. 953 timestampNanos = result.getUpdateTimestampNanos(); 954 } 955 956 ResultType clientResult = propertyResultCallback.build( 957 requestId, propertyId, areaId, timestampNanos, 958 carPropertyValue == null ? null : carPropertyValue.getValue()); 959 callbackExecutor.execute(() -> propertyResultCallback.onSuccess( 960 clientCallback, clientResult)); 961 } else { 962 callbackExecutor.execute(() -> propertyResultCallback.onFailure(clientCallback, 963 new PropertyAsyncError(requestId, propertyId, areaId, errorCode, 964 result.getVendorErrorCode()))); 965 } 966 } 967 } 968 } 969 970 /** 971 * A class to store async get/set property request info. 972 */ 973 private static final class AsyncPropertyRequestInfo<RequestType, CallbackType> { 974 private final RequestType mRequest; 975 private final Executor mCallbackExecutor; 976 private final CallbackType mCallback; 977 getRequest()978 public RequestType getRequest() { 979 return mRequest; 980 } 981 getCallbackExecutor()982 public Executor getCallbackExecutor() { 983 return mCallbackExecutor; 984 } 985 getCallback()986 public CallbackType getCallback() { 987 return mCallback; 988 } 989 AsyncPropertyRequestInfo(RequestType request, Executor callbackExecutor, CallbackType callback)990 private AsyncPropertyRequestInfo(RequestType request, Executor callbackExecutor, 991 CallbackType callback) { 992 mRequest = request; 993 mCallbackExecutor = callbackExecutor; 994 mCallback = callback; 995 } 996 } 997 998 /** Read ONCHANGE sensors. */ 999 @AddedInOrBefore(majorVersion = 33) 1000 public static final float SENSOR_RATE_ONCHANGE = 0f; 1001 /** Read sensors at the rate of 1 hertz */ 1002 @AddedInOrBefore(majorVersion = 33) 1003 public static final float SENSOR_RATE_NORMAL = 1f; 1004 /** Read sensors at the rate of 5 hertz */ 1005 @AddedInOrBefore(majorVersion = 33) 1006 public static final float SENSOR_RATE_UI = 5f; 1007 /** Read sensors at the rate of 10 hertz */ 1008 @AddedInOrBefore(majorVersion = 33) 1009 public static final float SENSOR_RATE_FAST = 10f; 1010 /** Read sensors at the rate of 100 hertz */ 1011 @AddedInOrBefore(majorVersion = 33) 1012 public static final float SENSOR_RATE_FASTEST = 100f; 1013 1014 1015 1016 /** 1017 * Status to indicate that set operation failed. Try it again. 1018 */ 1019 @AddedInOrBefore(majorVersion = 33) 1020 public static final int CAR_SET_PROPERTY_ERROR_CODE_TRY_AGAIN = 1; 1021 1022 /** 1023 * Status to indicate that set operation failed because of an invalid argument. 1024 */ 1025 @AddedInOrBefore(majorVersion = 33) 1026 public static final int CAR_SET_PROPERTY_ERROR_CODE_INVALID_ARG = 2; 1027 1028 /** 1029 * Status to indicate that set operation failed because the property is not available. 1030 */ 1031 @AddedInOrBefore(majorVersion = 33) 1032 public static final int CAR_SET_PROPERTY_ERROR_CODE_PROPERTY_NOT_AVAILABLE = 3; 1033 1034 /** 1035 * Status to indicate that set operation failed because car denied access to the property. 1036 */ 1037 @AddedInOrBefore(majorVersion = 33) 1038 public static final int CAR_SET_PROPERTY_ERROR_CODE_ACCESS_DENIED = 4; 1039 1040 /** 1041 * Status to indicate that set operation failed because of a general error in cars. 1042 */ 1043 @AddedInOrBefore(majorVersion = 33) 1044 public static final int CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN = 5; 1045 1046 /** @hide */ 1047 @IntDef(prefix = {"CAR_SET_PROPERTY_ERROR_CODE_"}, value = { 1048 CAR_SET_PROPERTY_ERROR_CODE_TRY_AGAIN, 1049 CAR_SET_PROPERTY_ERROR_CODE_INVALID_ARG, 1050 CAR_SET_PROPERTY_ERROR_CODE_PROPERTY_NOT_AVAILABLE, 1051 CAR_SET_PROPERTY_ERROR_CODE_ACCESS_DENIED, 1052 CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN, 1053 }) 1054 @Retention(RetentionPolicy.SOURCE) 1055 public @interface CarSetPropertyErrorCode {} 1056 1057 /** 1058 * Error indicating that there is an error detected in cars. 1059 */ 1060 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 1061 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 1062 public static final int STATUS_ERROR_INTERNAL_ERROR = 1; 1063 /** 1064 * Error indicating that the property is temporarily not available. 1065 */ 1066 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 1067 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 1068 public static final int STATUS_ERROR_NOT_AVAILABLE = 2; 1069 /** 1070 * Error indicating the operation has timed-out. 1071 */ 1072 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 1073 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 1074 public static final int STATUS_ERROR_TIMEOUT = 3; 1075 1076 /** @hide */ 1077 @IntDef(prefix = {"STATUS_"}, value = { 1078 STATUS_OK, 1079 STATUS_ERROR_INTERNAL_ERROR, 1080 STATUS_ERROR_NOT_AVAILABLE, 1081 STATUS_ERROR_TIMEOUT 1082 }) 1083 @Retention(RetentionPolicy.SOURCE) 1084 public @interface CarPropertyAsyncErrorCode {} 1085 1086 /** 1087 * Get an instance of the CarPropertyManager. 1088 * 1089 * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead. 1090 * 1091 * @param car the Car instance 1092 * @param service the ICarProperty instance 1093 * @hide 1094 */ CarPropertyManager(Car car, @NonNull ICarProperty service)1095 public CarPropertyManager(Car car, @NonNull ICarProperty service) { 1096 super(car); 1097 mService = service; 1098 mAppTargetSdk = getContext().getApplicationInfo().targetSdkVersion; 1099 1100 Handler eventHandler = getEventHandler(); 1101 if (eventHandler == null) { 1102 mHandler = null; 1103 return; 1104 } 1105 mHandler = new SingleMessageHandler<CarPropertyEvent>(eventHandler.getLooper(), 1106 MSG_GENERIC_EVENT) { 1107 @Override 1108 protected void handleEvent(CarPropertyEvent carPropertyEvent) { 1109 CarPropertyEventCallbackController carPropertyEventCallbackController; 1110 synchronized (mLock) { 1111 carPropertyEventCallbackController = 1112 mPropertyIdToCarPropertyEventCallbackController.get( 1113 carPropertyEvent.getCarPropertyValue().getPropertyId()); 1114 } 1115 if (carPropertyEventCallbackController == null) { 1116 return; 1117 } 1118 switch (carPropertyEvent.getEventType()) { 1119 case CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE: 1120 carPropertyEventCallbackController.forwardPropertyChanged(carPropertyEvent); 1121 break; 1122 case CarPropertyEvent.PROPERTY_EVENT_ERROR: 1123 carPropertyEventCallbackController.forwardErrorEvent(carPropertyEvent); 1124 break; 1125 default: 1126 throw new IllegalArgumentException(); 1127 } 1128 } 1129 }; 1130 } 1131 1132 /** 1133 * Register {@link CarPropertyEventCallback} to get property updates. Multiple callbacks 1134 * can be registered for a single property or the same callback can be used for different 1135 * properties. If the same callback is registered again for the same property, it will be 1136 * updated to new {@code updateRateHz}. 1137 * 1138 * <p>Rate could be one of the following: 1139 * <ul> 1140 * <li>{@link CarPropertyManager#SENSOR_RATE_ONCHANGE}</li> 1141 * <li>{@link CarPropertyManager#SENSOR_RATE_NORMAL}</li> 1142 * <li>{@link CarPropertyManager#SENSOR_RATE_UI}</li> 1143 * <li>{@link CarPropertyManager#SENSOR_RATE_FAST}</li> 1144 * <li>{@link CarPropertyManager#SENSOR_RATE_FASTEST}</li> 1145 * </ul> 1146 * 1147 * <p> 1148 * <b>Note:</b>Rate has no effect if the property has one of the following change modes: 1149 * <ul> 1150 * <li>{@link CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_STATIC}</li> 1151 * <li>{@link CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE}</li> 1152 * </ul> 1153 * 1154 * <p> 1155 * <b>Note:</b> If listener registers a callback for updates for a property for the first time, 1156 * it will receive the property's current value via a change event upon registration if the 1157 * property's value is currently available for reading. If the property is currently not 1158 * available for reading or in error state, a property change event with a unavailable or 1159 * error status will be generated. 1160 * 1161 * <p>For properties that might be unavailable for reading because their power state 1162 * is off, property change events containing the property's initial value will be generated 1163 * once their power state is on. 1164 * 1165 * <p>If {@code updateRateHz} is higher than {@link CarPropertyConfig#getMaxSampleRate()}, it 1166 * will be registered with max sample {@code updateRateHz}. 1167 * 1168 * <p>If {@code updateRateHz} is lower than {@link CarPropertyConfig#getMinSampleRate()}, it 1169 * will be registered with min sample {@code updateRateHz}. 1170 * 1171 * <p> 1172 * <b>Note:</b>Caller must check the value of {@link CarPropertyValue#getStatus()} for property 1173 * change events and only use {@link CarPropertyValue#getValue()} when 1174 * {@link CarPropertyValue#getStatus()} is {@link CarPropertyValue#STATUS_AVAILABLE}. If not, 1175 * the {@link CarPropertyValue#getValue()} is meaningless. 1176 * 1177 * <p> 1178 * <b>Note:</b>A property change event may/may not happen when the property's status 1179 * changes. Caller should not depend on the change event to check property's status. For 1180 * properties that might be unavailable because they depend on certain power state, caller 1181 * should subscribe to the power state property (e.g. 1182 * {@link VehiclePropertyIds#HVAC_POWER_ON} for hvac power dependent properties) to decide this 1183 * property's availability. 1184 * 1185 * @param carPropertyEventCallback the CarPropertyEventCallback to be registered 1186 * @param propertyId the property ID to subscribe 1187 * @param updateRateHz how fast the property events are delivered in Hz 1188 * @return {@code true} if the listener is successfully registered 1189 * @throws SecurityException if missing the appropriate permission. 1190 */ 1191 @AddedInOrBefore(majorVersion = 33) 1192 @SuppressWarnings("FormatString") registerCallback(@onNull CarPropertyEventCallback carPropertyEventCallback, int propertyId, @FloatRange(from = 0.0, to = 100.0) float updateRateHz)1193 public boolean registerCallback(@NonNull CarPropertyEventCallback carPropertyEventCallback, 1194 int propertyId, @FloatRange(from = 0.0, to = 100.0) float updateRateHz) { 1195 if (DBG) { 1196 Log.d(TAG, String.format("registerCallback, callback: %s propertyId: %s, " 1197 + "updateRateHz: %f", carPropertyEventCallback, 1198 VehiclePropertyIds.toString(propertyId), updateRateHz)); 1199 } 1200 requireNonNull(carPropertyEventCallback); 1201 CarPropertyConfig<?> carPropertyConfig = getCarPropertyConfig(propertyId); 1202 if (carPropertyConfig == null) { 1203 Log.e(TAG, "registerCallback: propertyId is not in carPropertyConfig list: " 1204 + VehiclePropertyIds.toString(propertyId)); 1205 return false; 1206 } 1207 1208 float sanitizedUpdateRateHz = InputSanitizationUtils.sanitizeUpdateRateHz(carPropertyConfig, 1209 updateRateHz); 1210 1211 boolean registerSuccessful; 1212 synchronized (mLock) { 1213 boolean isNewInstance = false; 1214 CarPropertyEventCallbackController carPropertyEventCallbackController = 1215 mPropertyIdToCarPropertyEventCallbackController.get(propertyId); 1216 if (carPropertyEventCallbackController == null) { 1217 carPropertyEventCallbackController = new CarPropertyEventCallbackController( 1218 propertyId, mLock, mRegistrationUpdateCallback); 1219 isNewInstance = true; 1220 } 1221 1222 registerSuccessful = carPropertyEventCallbackController.add(carPropertyEventCallback, 1223 sanitizedUpdateRateHz); 1224 if (registerSuccessful && isNewInstance) { 1225 mPropertyIdToCarPropertyEventCallbackController.put(propertyId, 1226 carPropertyEventCallbackController); 1227 } 1228 } 1229 return registerSuccessful; 1230 } 1231 1232 private static class CarPropertyEventListenerToService extends ICarPropertyEventListener.Stub { 1233 private final WeakReference<CarPropertyManager> mCarPropertyManager; 1234 CarPropertyEventListenerToService(CarPropertyManager carPropertyManager)1235 CarPropertyEventListenerToService(CarPropertyManager carPropertyManager) { 1236 mCarPropertyManager = new WeakReference<>(carPropertyManager); 1237 } 1238 1239 @Override onEvent(List<CarPropertyEvent> carPropertyEvents)1240 public void onEvent(List<CarPropertyEvent> carPropertyEvents) throws RemoteException { 1241 CarPropertyManager carPropertyManager = mCarPropertyManager.get(); 1242 if (carPropertyManager != null) { 1243 carPropertyManager.handleEvents(carPropertyEvents); 1244 } 1245 } 1246 } 1247 handleEvents(List<CarPropertyEvent> carPropertyEvents)1248 private void handleEvents(List<CarPropertyEvent> carPropertyEvents) { 1249 if (mHandler != null) { 1250 mHandler.sendEvents(carPropertyEvents); 1251 } 1252 } 1253 1254 /** 1255 * Stop getting property updates for the given {@link CarPropertyEventCallback}. If there are 1256 * multiple registrations for this {@link CarPropertyEventCallback}, all listening will be 1257 * stopped. 1258 * 1259 * @throws SecurityException if missing the appropriate permission. 1260 */ 1261 @AddedInOrBefore(majorVersion = 33) unregisterCallback(@onNull CarPropertyEventCallback carPropertyEventCallback)1262 public void unregisterCallback(@NonNull CarPropertyEventCallback carPropertyEventCallback) { 1263 if (DBG) { 1264 Log.d(TAG, "unregisterCallback, callback: " + carPropertyEventCallback); 1265 } 1266 requireNonNull(carPropertyEventCallback); 1267 int[] propertyIds; 1268 synchronized (mLock) { 1269 propertyIds = new int[mPropertyIdToCarPropertyEventCallbackController.size()]; 1270 for (int i = 0; i < mPropertyIdToCarPropertyEventCallbackController.size(); i++) { 1271 propertyIds[i] = mPropertyIdToCarPropertyEventCallbackController.keyAt(i); 1272 } 1273 } 1274 for (int propertyId : propertyIds) { 1275 unregisterCallback(carPropertyEventCallback, propertyId); 1276 } 1277 } 1278 1279 /** 1280 * Stop getting update for {@code propertyId} to the given {@link CarPropertyEventCallback}. If 1281 * the same {@link CarPropertyEventCallback} is used for other properties, those subscriptions 1282 * will not be affected. 1283 * 1284 * @throws SecurityException if missing the appropriate permission. 1285 */ 1286 @AddedInOrBefore(majorVersion = 33) 1287 @SuppressWarnings("FormatString") unregisterCallback(@onNull CarPropertyEventCallback carPropertyEventCallback, int propertyId)1288 public void unregisterCallback(@NonNull CarPropertyEventCallback carPropertyEventCallback, 1289 int propertyId) { 1290 if (DBG) { 1291 Log.d(TAG, String.format("unregisterCallback, callback: %s, property Id: %s", 1292 carPropertyEventCallback, VehiclePropertyIds.toString(propertyId))); 1293 } 1294 requireNonNull(carPropertyEventCallback); 1295 if (!CarPropertyHelper.isSupported(propertyId)) { 1296 Log.e(TAG, "unregisterCallback: propertyId: " 1297 + VehiclePropertyIds.toString(propertyId) + " is not supported"); 1298 return; 1299 } 1300 synchronized (mLock) { 1301 CarPropertyEventCallbackController carPropertyEventCallbackController = 1302 mPropertyIdToCarPropertyEventCallbackController.get(propertyId); 1303 1304 if (carPropertyEventCallbackController == null) { 1305 return; 1306 } 1307 1308 boolean allCallbacksRemoved = carPropertyEventCallbackController.remove( 1309 carPropertyEventCallback); 1310 if (allCallbacksRemoved) { 1311 mPropertyIdToCarPropertyEventCallbackController.remove(propertyId); 1312 } 1313 } 1314 } 1315 1316 /** 1317 * @return the list of properties supported by this car that the application may access 1318 */ 1319 @NonNull 1320 @AddedInOrBefore(majorVersion = 33) getPropertyList()1321 public List<CarPropertyConfig> getPropertyList() { 1322 if (DBG) { 1323 Log.d(TAG, "getPropertyList"); 1324 } 1325 List<CarPropertyConfig> configs; 1326 try { 1327 configs = mService.getPropertyList().getConfigs(); 1328 } catch (RemoteException e) { 1329 Log.e(TAG, "getPropertyList exception ", e); 1330 return handleRemoteExceptionFromCarService(e, new ArrayList<>()); 1331 } 1332 if (DBG) { 1333 Log.d(TAG, "getPropertyList returns " + configs.size() + " configs"); 1334 for (int i = 0; i < configs.size(); i++) { 1335 Log.v(TAG, i + ": " + configs.get(i)); 1336 } 1337 } 1338 return configs; 1339 } 1340 1341 /** 1342 * Checks the given property IDs and returns a list of property configs supported by the car. 1343 * 1344 * If some of the properties in the given ID list are not supported, they will not be returned. 1345 * 1346 * @param propertyIds the list of property IDs 1347 * @return the list of property configs 1348 */ 1349 @NonNull 1350 @AddedInOrBefore(majorVersion = 33) getPropertyList(@onNull ArraySet<Integer> propertyIds)1351 public List<CarPropertyConfig> getPropertyList(@NonNull ArraySet<Integer> propertyIds) { 1352 if (DBG) { 1353 Log.d(TAG, "getPropertyList(" + CarPropertyHelper.propertyIdsToString(propertyIds) 1354 + ")"); 1355 } 1356 List<Integer> filteredPropertyIds = new ArrayList<>(); 1357 for (int propertyId : propertyIds) { 1358 if (!CarPropertyHelper.isSupported(propertyId)) { 1359 continue; 1360 } 1361 filteredPropertyIds.add(propertyId); 1362 } 1363 int[] filteredPropertyIdsArray = new int[filteredPropertyIds.size()]; 1364 for (int i = 0; i < filteredPropertyIds.size(); i++) { 1365 filteredPropertyIdsArray[i] = filteredPropertyIds.get(i); 1366 } 1367 try { 1368 List<CarPropertyConfig> configs = mService.getPropertyConfigList( 1369 filteredPropertyIdsArray).getConfigs(); 1370 if (DBG) { 1371 Log.d(TAG, "getPropertyList(" + CarPropertyHelper.propertyIdsToString(propertyIds) 1372 + ") returns " + configs.size() + " configs"); 1373 for (int i = 0; i < configs.size(); i++) { 1374 Log.v(TAG, i + ": " + configs.get(i)); 1375 } 1376 } 1377 return configs; 1378 } catch (RemoteException e) { 1379 Log.e(TAG, "getPropertyList exception ", e); 1380 return handleRemoteExceptionFromCarService(e, new ArrayList<>()); 1381 } 1382 } 1383 1384 /** 1385 * Get {@link CarPropertyConfig} by property ID. 1386 * 1387 * @param propertyId the property ID 1388 * @return the {@link CarPropertyConfig} for the selected property, {@code null} if the property 1389 * is not available 1390 */ 1391 @Nullable 1392 @AddedInOrBefore(majorVersion = 33) getCarPropertyConfig(int propertyId)1393 public CarPropertyConfig<?> getCarPropertyConfig(int propertyId) { 1394 if (DBG) { 1395 Log.d(TAG, "getCarPropertyConfig(" + VehiclePropertyIds.toString(propertyId) + ")"); 1396 } 1397 if (!CarPropertyHelper.isSupported(propertyId)) { 1398 Log.w(TAG, "Property: " + VehiclePropertyIds.toString(propertyId) 1399 + " is not supported"); 1400 return null; 1401 } 1402 List<CarPropertyConfig> configs; 1403 try { 1404 configs = mService.getPropertyConfigList(new int[] {propertyId}).getConfigs(); 1405 } catch (RemoteException e) { 1406 Log.e(TAG, "getPropertyList exception ", e); 1407 return handleRemoteExceptionFromCarService(e, null); 1408 } 1409 CarPropertyConfig<?> config = configs.size() == 0 ? null : configs.get(0); 1410 if (DBG) { 1411 Log.d(TAG, "getCarPropertyConfig(" + VehiclePropertyIds.toString(propertyId) 1412 + ") returns " + config); 1413 } 1414 return config; 1415 } 1416 1417 /** 1418 * Returns areaId contains the selected area for the property. 1419 * 1420 * @param propertyId the property ID 1421 * @param area the area enum such as Enums in {@link android.car.VehicleAreaSeat} 1422 * @throws IllegalArgumentException if the property is not available in the vehicle for 1423 * the selected area 1424 * @return the {@code AreaId} containing the selected area for the property 1425 */ 1426 @AddedInOrBefore(majorVersion = 33) getAreaId(int propertyId, int area)1427 public int getAreaId(int propertyId, int area) { 1428 String propertyIdStr = VehiclePropertyIds.toString(propertyId); 1429 if (DBG) { 1430 Log.d(TAG, "getAreaId(propertyId = " + propertyIdStr + ", area = " + area + ")"); 1431 } 1432 CarPropertyConfig<?> propConfig = getCarPropertyConfig(propertyId); 1433 if (propConfig == null) { 1434 throw new IllegalArgumentException("The propertyId: " + propertyIdStr 1435 + " is not available"); 1436 } 1437 // For the global property, areaId is 0 1438 if (propConfig.isGlobalProperty()) { 1439 if (DBG) { 1440 Log.d(TAG, "getAreaId returns the global area ID (0)"); 1441 } 1442 return 0; 1443 } 1444 for (int areaId : propConfig.getAreaIds()) { 1445 if ((area & areaId) == area) { 1446 if (DBG) { 1447 Log.d(TAG, "getAreaId returns " + areaId); 1448 } 1449 return areaId; 1450 } 1451 } 1452 1453 throw new IllegalArgumentException("The propertyId: " + propertyIdStr 1454 + " is not available at the area: 0x" + toHexString(area)); 1455 } 1456 1457 /** 1458 * Return read permission string for given property ID. 1459 * 1460 * @param propId the property ID to query 1461 * @return the permission needed to read this property, {@code null} if the property ID is not 1462 * available 1463 * 1464 * @hide 1465 */ 1466 @Nullable 1467 @AddedInOrBefore(majorVersion = 33) getReadPermission(int propId)1468 public String getReadPermission(int propId) { 1469 try { 1470 String permission = mService.getReadPermission(propId); 1471 if (DBG) { 1472 Log.d(TAG, "getReadPermission(propId =" + VehiclePropertyIds.toString(propId) 1473 + ") returns " + permission); 1474 } 1475 return permission; 1476 } catch (RemoteException e) { 1477 return handleRemoteExceptionFromCarService(e, ""); 1478 } 1479 } 1480 1481 /** 1482 * Return write permission string for given property ID. 1483 * 1484 * @param propId the property ID to query 1485 * @return the permission needed to write this property, {@code null} if the property ID is not 1486 * available. 1487 * 1488 * @hide 1489 */ 1490 @Nullable 1491 @AddedInOrBefore(majorVersion = 33) getWritePermission(int propId)1492 public String getWritePermission(int propId) { 1493 try { 1494 String permission = mService.getWritePermission(propId); 1495 if (DBG) { 1496 Log.d(TAG, "getWritePermission(propId = " + VehiclePropertyIds.toString(propId) 1497 + ") returns " + permission); 1498 } 1499 return permission; 1500 } catch (RemoteException e) { 1501 return handleRemoteExceptionFromCarService(e, ""); 1502 } 1503 } 1504 1505 1506 /** 1507 * Check whether a given property is available or disabled based on the car's current state. 1508 * 1509 * @param propertyId the property ID 1510 * @param areaId the area ID 1511 * @return {@code true} if {@link CarPropertyValue#STATUS_AVAILABLE}, {@code false} otherwise 1512 * (eg {@link CarPropertyValue#STATUS_UNAVAILABLE}) 1513 */ 1514 @AddedInOrBefore(majorVersion = 33) isPropertyAvailable(int propertyId, int areaId)1515 public boolean isPropertyAvailable(int propertyId, int areaId) { 1516 if (DBG) { 1517 Log.d(TAG, "isPropertyAvailable(propertyId = " 1518 + VehiclePropertyIds.toString(propertyId) + ", areaId = " + areaId + ")"); 1519 } 1520 if (!CarPropertyHelper.isSupported(propertyId)) { 1521 if (DBG) { 1522 Log.d(TAG, "Property: " + VehiclePropertyIds.toString(propertyId) 1523 + " is not supported"); 1524 } 1525 return false; 1526 } 1527 1528 try { 1529 CarPropertyValue propValue = runSyncOperation(() -> { 1530 if (DBG) { 1531 Log.d(TAG, "calling getProperty to check property's availability"); 1532 } 1533 return mService.getProperty(propertyId, areaId); 1534 }); 1535 return (propValue != null 1536 && propValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE); 1537 } catch (RemoteException e) { 1538 return handleRemoteExceptionFromCarService(e, false); 1539 } catch (ServiceSpecificException e) { 1540 Log.e(TAG, "unable to get property, error: " + e); 1541 return false; 1542 } 1543 } 1544 1545 /** 1546 * Returns value of a bool property 1547 * 1548 * <p>This method may take couple seconds to complete, so it needs to be called from a 1549 * non-main thread. 1550 * 1551 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} equal 1552 * or later than {@link Build.VERSION_CODES#R} will receive the following exceptions when 1553 * request is failed. 1554 * <ul> 1555 * <li>{@link CarInternalErrorException} 1556 * <li>{@link PropertyAccessDeniedSecurityException} 1557 * <li>{@link PropertyNotAvailableAndRetryException} 1558 * <li>{@link PropertyNotAvailableException} 1559 * <li>{@link IllegalArgumentException} 1560 * </ul> 1561 * 1562 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} 1563 * earlier than {@link Build.VERSION_CODES#R} will receive the following exceptions if the call 1564 * fails. 1565 * <ul> 1566 * <li>{@link IllegalStateException} when there is an error detected in cars, or when 1567 * cars denied the access of the property, or when the property is not available and 1568 * might be unavailable for a while, or when unexpected error happens. 1569 * <li>{@link IllegalArgumentException} when the [propertyId, areaId] is not supported. 1570 * </ul> 1571 * 1572 * <p>For pre-R client, the returned value is {@code false} if the property is temporarily not 1573 * available. 1574 * 1575 * @param propertyId the property ID to get 1576 * @param areaId the area ID of the property to get 1577 * 1578 * @throws CarInternalErrorException when there is an unexpected error detected in cars 1579 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the 1580 * property 1581 * @throws PropertyNotAvailableAndRetryException when the property is temporarily 1582 * not available and likely that retrying will be successful 1583 * @throws PropertyNotAvailableException when the property is not available and might be 1584 * unavailable for a while. 1585 * @throws IllegalArgumentException when the [propertyId, areaId] is not supported. 1586 * 1587 * @return the value of a bool property, or {@code false} for pre-R client if the property is 1588 * temporarily not available 1589 */ 1590 @AddedInOrBefore(majorVersion = 33) getBooleanProperty(int propertyId, int areaId)1591 public boolean getBooleanProperty(int propertyId, int areaId) { 1592 CarPropertyValue<Boolean> carProp = getProperty(Boolean.class, propertyId, areaId); 1593 return handleNullAndPropertyStatus(carProp, areaId, false); 1594 } 1595 1596 /** 1597 * Returns value of a float property 1598 * 1599 * <p>This method may take couple seconds to complete, so it needs to be called from a 1600 * non-main thread. 1601 * 1602 * <p>This method has the same exception behavior as {@link #getBooleanProperty(int, int)}. 1603 * 1604 * @param propertyId the property ID to get 1605 * @param areaId the area ID of the property to get 1606 * 1607 * @throws CarInternalErrorException when there is an unexpected error detected in cars 1608 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the 1609 * property 1610 * @throws PropertyNotAvailableAndRetryException when the property is temporarily 1611 * not available and likely that retrying will be successful 1612 * @throws PropertyNotAvailableException when the property is not available and might be 1613 * unavailable for a while. 1614 * @throws IllegalArgumentException when the [propertyId, areaId] is not supported. 1615 * 1616 * @return the value of a float property, or 0 if client is pre-R and the property is 1617 * temporarily not available 1618 */ 1619 @AddedInOrBefore(majorVersion = 33) getFloatProperty(int propertyId, int areaId)1620 public float getFloatProperty(int propertyId, int areaId) { 1621 CarPropertyValue<Float> carProp = getProperty(Float.class, propertyId, areaId); 1622 return handleNullAndPropertyStatus(carProp, areaId, 0f); 1623 } 1624 1625 /** 1626 * Returns value of an integer property 1627 * 1628 * <p>This method may take couple seconds to complete, so it needs to be called form a 1629 * non-main thread. 1630 * 1631 * <p>This method has the same exception behavior as {@link #getBooleanProperty(int, int)}. 1632 * 1633 * @param propertyId the property ID to get 1634 * @param areaId the area ID of the property to get 1635 * 1636 * @throws CarInternalErrorException when there is an unexpected error detected in cars 1637 * @throws PropertyAccessDeniedSecurityException} when cars denied the access of the 1638 * property 1639 * @throws PropertyNotAvailableAndRetryException} when the property is temporarily 1640 * not available and likely that retrying will be successful 1641 * @throws PropertyNotAvailableException when the property is not available and might be 1642 * unavailable for a while. 1643 * @throws IllegalArgumentException when the [propertyId, areaId] is not supported. 1644 * 1645 * @return the value of a integer property, or 0 if client is pre-R and the property is 1646 * temporarily not available 1647 */ 1648 @AddedInOrBefore(majorVersion = 33) getIntProperty(int propertyId, int areaId)1649 public int getIntProperty(int propertyId, int areaId) { 1650 CarPropertyValue<Integer> carProp = getProperty(Integer.class, propertyId, areaId); 1651 return handleNullAndPropertyStatus(carProp, areaId, 0); 1652 } 1653 1654 /** 1655 * Returns value of an integer array property 1656 * 1657 * <p>This method may take couple seconds to complete, so it needs to be called from a 1658 * non-main thread. 1659 * 1660 * <p>This method has the same exception behavior as {@link #getBooleanProperty(int, int)}. 1661 * 1662 * @param propertyId the property ID to get 1663 * @param areaId the area ID of the property to get 1664 * 1665 * @throws CarInternalErrorException when there is an unexpected error detected in cars 1666 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the 1667 * property 1668 * @throws PropertyNotAvailableAndRetryException} when the property is temporarily 1669 * not available and likely that retrying will be successful 1670 * @throws PropertyNotAvailableException} when the property is not available and might be 1671 * unavailable for a while. 1672 * @throws IllegalArgumentException} when the [propertyId, areaId] is not supported. 1673 * 1674 * @return the value of a integer array property, or an empty integer array if client is pre-R 1675 * and the property is temporarily not available 1676 */ 1677 @NonNull 1678 @AddedInOrBefore(majorVersion = 33) getIntArrayProperty(int propertyId, int areaId)1679 public int[] getIntArrayProperty(int propertyId, int areaId) { 1680 CarPropertyValue<Integer[]> carProp = getProperty(Integer[].class, propertyId, areaId); 1681 Integer[] res = handleNullAndPropertyStatus(carProp, areaId, new Integer[0]); 1682 return toIntArray(res); 1683 } 1684 toIntArray(Integer[] input)1685 private static int[] toIntArray(Integer[] input) { 1686 int len = input.length; 1687 int[] arr = new int[len]; 1688 for (int i = 0; i < len; i++) { 1689 arr[i] = input[i]; 1690 } 1691 return arr; 1692 } 1693 handleNullAndPropertyStatus(CarPropertyValue<T> propertyValue, int areaId, T defaultValue)1694 private <T> T handleNullAndPropertyStatus(CarPropertyValue<T> propertyValue, int areaId, 1695 T defaultValue) { 1696 if (propertyValue == null) { 1697 return defaultValue; 1698 } 1699 1700 // Keeps the same behavior as android R. 1701 if (mAppTargetSdk < Build.VERSION_CODES.S) { 1702 return propertyValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE 1703 ? propertyValue.getValue() : defaultValue; 1704 } 1705 1706 // throws new exceptions in android S. 1707 switch (propertyValue.getStatus()) { 1708 case CarPropertyValue.STATUS_ERROR: 1709 throw new CarInternalErrorException(propertyValue.getPropertyId(), areaId); 1710 case CarPropertyValue.STATUS_UNAVAILABLE: 1711 throw new PropertyNotAvailableException(propertyValue.getPropertyId(), 1712 areaId, /*vendorErrorCode=*/0); 1713 default: 1714 return propertyValue.getValue(); 1715 } 1716 } 1717 1718 @FunctionalInterface 1719 private interface RemoteCallable<V> { call()1720 V call() throws RemoteException; 1721 } 1722 1723 @Nullable runSyncOperation(RemoteCallable<V> c)1724 private static <V> V runSyncOperation(RemoteCallable<V> c) 1725 throws RemoteException, ServiceSpecificException { 1726 int retryCount = 0; 1727 while (retryCount < SYNC_OP_RETRY_MAX_COUNT) { 1728 retryCount++; 1729 try { 1730 return c.call(); 1731 } catch (ServiceSpecificException e) { 1732 if (e.errorCode != SYNC_OP_LIMIT_TRY_AGAIN) { 1733 throw e; 1734 } 1735 // If car service don't have enough binder thread to handle this request. Sleep for 1736 // 10ms and try again. 1737 Log.d(TAG, "too many sync request, sleeping for " + SYNC_OP_RETRY_SLEEP_IN_MS 1738 + " ms before retry"); 1739 SystemClock.sleep(SYNC_OP_RETRY_SLEEP_IN_MS); 1740 continue; 1741 } catch (RemoteException e) { 1742 throw e; 1743 } 1744 } 1745 throw new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR, 1746 "failed to call car service sync operations after " + retryCount + " retries"); 1747 } 1748 1749 /** 1750 * Return {@link CarPropertyValue} 1751 * 1752 * <p>This method may take couple seconds to complete, so it needs to be called from a 1753 * non-main thread. 1754 * 1755 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} equal 1756 * or later than {@link Build.VERSION_CODES#R} will receive the following exceptions when 1757 * request is failed. 1758 * <ul> 1759 * <li>{@link CarInternalErrorException} 1760 * <li>{@link PropertyAccessDeniedSecurityException} 1761 * <li>{@link PropertyNotAvailableAndRetryException} 1762 * <li>{@link PropertyNotAvailableException} 1763 * <li>{@link IllegalArgumentException} 1764 * </ul> 1765 * 1766 * <p>For R or later version client, the returned value will never be null. 1767 * 1768 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} 1769 * earlier than {@link Build.VERSION_CODES#R} will receive the following exceptions when request 1770 * is failed. 1771 * <ul> 1772 * <li>{@link IllegalStateException} when there is an error detected in cars, or when 1773 * cars denied the access of the property, or when the property is not available and 1774 * might be unavailable for a while, or when unexpected error happens. 1775 * <li>{@link IllegalArgumentException} when the [propertyId, areaId] is not supported. 1776 * </ul> 1777 * 1778 * <p>For pre-R client, the returned value might be null if the property is temporarily not 1779 * available. The client should try again in this case. 1780 * 1781 * @param clazz the class object for the CarPropertyValue 1782 * @param propertyId the property ID to get 1783 * @param areaId the area ID of the property to get 1784 * 1785 * @throws CarInternalErrorException when there is an unexpected error detected in cars 1786 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the 1787 * property 1788 * @throws PropertyNotAvailableAndRetryException when the property is temporarily 1789 * not available and likely that retrying will be successful 1790 * @throws PropertyNotAvailableException when the property is not available and might be 1791 * unavailable for a while. 1792 * @throws IllegalArgumentException when the [propertyId, areaId] is not supported. 1793 * 1794 * @return the value of a property 1795 */ 1796 @SuppressWarnings("unchecked") 1797 @Nullable 1798 @AddedInOrBefore(majorVersion = 33) getProperty(@onNull Class<E> clazz, int propertyId, int areaId)1799 public <E> CarPropertyValue<E> getProperty(@NonNull Class<E> clazz, int propertyId, 1800 int areaId) { 1801 CarPropertyValue<E> carPropertyValue = getProperty(propertyId, areaId); 1802 if (carPropertyValue == null) { 1803 return null; 1804 } 1805 Class<?> actualClass = carPropertyValue.getValue().getClass(); 1806 if (actualClass != clazz) { 1807 throw new IllegalArgumentException( 1808 "Invalid property type. " + "Expected: " + clazz + ", but was: " 1809 + actualClass); 1810 } 1811 return carPropertyValue; 1812 } 1813 1814 /** 1815 * Query {@link CarPropertyValue} with property id and areaId. 1816 * 1817 * <p>This method may take couple seconds to complete, so it needs to be called from a 1818 * non-main thread. 1819 * 1820 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} equal 1821 * or later than {@link Build.VERSION_CODES#R} will receive the following exceptions when 1822 * request is failed. 1823 * <ul> 1824 * <li>{@link CarInternalErrorException} 1825 * <li>{@link PropertyAccessDeniedSecurityException} 1826 * <li>{@link PropertyNotAvailableAndRetryException} 1827 * <li>{@link PropertyNotAvailableException} 1828 * <li>{@link IllegalArgumentException} 1829 * </ul> 1830 * 1831 * <p>For R or later version client, the returned value will never be null. 1832 * 1833 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} 1834 * earlier than {@link Build.VERSION_CODES#R} will receive the following exceptions when request 1835 * is failed. 1836 * <ul> 1837 * <li>{@link IllegalStateException} when there is an error detected in cars, or when 1838 * cars denied the access of the property, or when the property is not available and 1839 * might be unavailable for a while, or when unexpected error happens. 1840 * <li>{@link IllegalArgumentException} when the [propertyId, areaId] is not supported. 1841 * </ul> 1842 * 1843 * <p>For pre-R client, the returned value might be null if the property is temporarily not 1844 * available. The client should try again in this case. 1845 * 1846 * @param propertyId the property ID to get 1847 * @param areaId the area ID of the property to get 1848 * @param <E> the class type of the property 1849 * 1850 * @throws CarInternalErrorException when there is an unexpected error detected in cars. 1851 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the 1852 * property. 1853 * @throws PropertyNotAvailableAndRetryException when the property is temporarily 1854 * not available and likely that retrying will be successful. 1855 * @throws PropertyNotAvailableException when the property is not available and might be 1856 * unavailable for a while. 1857 * @throws IllegalArgumentException when the [propertyId, areaId] is not supported. 1858 * 1859 * @return the value of a property 1860 */ 1861 @Nullable 1862 @AddedInOrBefore(majorVersion = 33) getProperty(int propertyId, int areaId)1863 public <E> CarPropertyValue<E> getProperty(int propertyId, int areaId) { 1864 if (DBG) { 1865 Log.d(TAG, "getProperty, propertyId: " + VehiclePropertyIds.toString(propertyId) 1866 + ", areaId: 0x" + toHexString(areaId)); 1867 } 1868 1869 assertPropertyIdIsSupported(propertyId); 1870 1871 Trace.beginSection("getProperty-" + propertyId + "/" + areaId); 1872 try { 1873 CarPropertyValue<E> carPropertyValue = (CarPropertyValue<E>) (runSyncOperation(() -> { 1874 return mService.getProperty(propertyId, areaId); 1875 })); 1876 if (carPropertyValue == null) { 1877 return null; 1878 } 1879 if (mAppTargetSdk >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 1880 if (carPropertyValue.getStatus() == CarPropertyValue.STATUS_UNAVAILABLE) { 1881 throw new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE, 1882 "getProperty returned value with UNAVAILABLE status: " 1883 + carPropertyValue); 1884 } else if (carPropertyValue.getStatus() != CarPropertyValue.STATUS_AVAILABLE) { 1885 throw new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR, 1886 "getProperty returned value with error or unknown status: " 1887 + carPropertyValue); 1888 } 1889 } 1890 return carPropertyValue; 1891 } catch (RemoteException e) { 1892 return handleRemoteExceptionFromCarService(e, null); 1893 } catch (ServiceSpecificException e) { 1894 if (DBG) { 1895 Log.d(TAG, "getProperty received service specific exception, code: " + e.errorCode); 1896 } 1897 if (mAppTargetSdk < Build.VERSION_CODES.R) { 1898 if (e.errorCode == VehicleHalStatusCode.STATUS_TRY_AGAIN) { 1899 return null; 1900 } else { 1901 throw new IllegalStateException("Failed to get propertyId: " 1902 + VehiclePropertyIds.toString(propertyId) + " areaId: 0x" 1903 + toHexString(areaId), e); 1904 } 1905 } 1906 handleCarServiceSpecificException(e, propertyId, areaId); 1907 1908 // Never reaches here. 1909 return null; 1910 } finally { 1911 Trace.endSection(); 1912 } 1913 } 1914 1915 /** 1916 * Set value of car property by areaId. 1917 * 1918 * <p>If multiple clients set a property for the same area ID simultaneously, which one takes 1919 * precedence is undefined. Typically, the last set operation (in the order that they are issued 1920 * to the car's ECU) overrides the previous set operations. 1921 * 1922 * <p>This method may take couple seconds to complete, so it needs to be called form a 1923 * non-main thread. 1924 * 1925 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} equal 1926 * or later than {@link Build.VERSION_CODES#R} will receive the following exceptions when 1927 * request is failed. 1928 * <ul> 1929 * <li>{@link CarInternalErrorException} 1930 * <li>{@link PropertyAccessDeniedSecurityException} 1931 * <li>{@link PropertyNotAvailableAndRetryException} 1932 * <li>{@link PropertyNotAvailableException} 1933 * <li>{@link IllegalArgumentException} 1934 * </ul> 1935 * <p>Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} 1936 * earlier than {@link Build.VERSION_CODES#R} will receive the following exceptions when request 1937 * is failed. 1938 * <ul> 1939 * <li>{@link RuntimeException} when the property is temporarily not available. 1940 * <li>{@link IllegalStateException} when there is an error detected in cars, or when 1941 * cars denied the access of the property, or when the property is not available and 1942 * might be unavailable for a while, or when unexpected error happens. 1943 * <li>{@link IllegalArgumentException} when the [propertyId, areaId] is not supported. 1944 * </ul> 1945 * 1946 * @param clazz the class object for the CarPropertyValue 1947 * @param propertyId the property ID to modify 1948 * @param areaId the area ID to apply the modification 1949 * @param val the value to set 1950 * @param <E> the class type of the given property, for example property that was 1951 * defined as {@code VEHICLE_VALUE_TYPE_INT32} in vehicle HAL could be accessed using 1952 * {@code Integer.class}. 1953 * 1954 * @throws CarInternalErrorException when there is an unexpected error detected in cars. 1955 * @throws PropertyAccessDeniedSecurityException when cars denied the access of the property. 1956 * @throws PropertyNotAvailableException when the property is not available and might be 1957 * unavailable for a while. 1958 * @throws PropertyNotAvailableAndRetryException when the property is temporarily not available 1959 * and likely that retrying will be successful. 1960 * @throws IllegalArgumentException when the [propertyId, areaId] is not supported. 1961 */ 1962 @AddedInOrBefore(majorVersion = 33) setProperty(@onNull Class<E> clazz, int propertyId, int areaId, @NonNull E val)1963 public <E> void setProperty(@NonNull Class<E> clazz, int propertyId, int areaId, 1964 @NonNull E val) { 1965 if (DBG) { 1966 Log.d(TAG, "setProperty, propertyId: " + VehiclePropertyIds.toString(propertyId) 1967 + ", areaId: 0x" + toHexString(areaId) + ", class: " + clazz + ", val: " + val); 1968 } 1969 1970 assertPropertyIdIsSupported(propertyId); 1971 1972 Trace.beginSection("setProperty-" + propertyId + "/" + areaId); 1973 try { 1974 runSyncOperation(() -> { 1975 mService.setProperty(new CarPropertyValue<>(propertyId, areaId, val), 1976 mCarPropertyEventToService); 1977 return null; 1978 }); 1979 } catch (RemoteException e) { 1980 handleRemoteExceptionFromCarService(e); 1981 return; 1982 } catch (ServiceSpecificException e) { 1983 if (DBG) { 1984 Log.d(TAG, "setProperty received service specific exception", e); 1985 } 1986 if (mAppTargetSdk < Build.VERSION_CODES.R) { 1987 if (e.errorCode == VehicleHalStatusCode.STATUS_TRY_AGAIN) { 1988 throw new RuntimeException("Failed to set propertyId: " 1989 + VehiclePropertyIds.toString(propertyId) + " areaId: 0x" 1990 + toHexString(areaId), e); 1991 } else { 1992 throw new IllegalStateException("Failed to set propertyId: " 1993 + VehiclePropertyIds.toString(propertyId) + " areaId: 0x" 1994 + toHexString(areaId), e); 1995 } 1996 } 1997 handleCarServiceSpecificException(e, propertyId, areaId); 1998 return; 1999 } finally { 2000 Trace.endSection(); 2001 } 2002 } 2003 2004 /** 2005 * Modifies a property. If the property modification doesn't occur, an error event shall be 2006 * generated and propagated back to the application. 2007 * 2008 * <p>This method may take couple seconds to complete, so it needs to be called from a 2009 * non-main thread. 2010 * 2011 * @param propertyId the property ID to modify 2012 * @param areaId the area ID to apply the modification 2013 * @param val the value to set 2014 */ 2015 @AddedInOrBefore(majorVersion = 33) setBooleanProperty(int propertyId, int areaId, boolean val)2016 public void setBooleanProperty(int propertyId, int areaId, boolean val) { 2017 setProperty(Boolean.class, propertyId, areaId, val); 2018 } 2019 2020 /** 2021 * Set float value of property 2022 * 2023 * <p>This method may take couple seconds to complete, so it needs to be called from a 2024 * non-main thread. 2025 * 2026 * @param propertyId the property ID to modify 2027 * @param areaId the area ID to apply the modification 2028 * @param val the value to set 2029 */ 2030 @AddedInOrBefore(majorVersion = 33) setFloatProperty(int propertyId, int areaId, float val)2031 public void setFloatProperty(int propertyId, int areaId, float val) { 2032 setProperty(Float.class, propertyId, areaId, val); 2033 } 2034 2035 /** 2036 * Set int value of property 2037 * 2038 * <p>This method may take couple seconds to complete, so it needs to be called from a 2039 * non-main thread. 2040 * 2041 * @param propertyId the property ID to modify 2042 * @param areaId the area ID to apply the modification 2043 * @param val the value to set 2044 */ 2045 @AddedInOrBefore(majorVersion = 33) setIntProperty(int propertyId, int areaId, int val)2046 public void setIntProperty(int propertyId, int areaId, int val) { 2047 setProperty(Integer.class, propertyId, areaId, val); 2048 } 2049 2050 /** 2051 * Handles {@code ServiceSpecificException} in {@code CarService} for R and later version. 2052 */ handleCarServiceSpecificException( ServiceSpecificException e, int propertyId, int areaId)2053 private void handleCarServiceSpecificException( 2054 ServiceSpecificException e, int propertyId, int areaId) { 2055 // We are not passing the error message down, so log it here. 2056 Log.w(TAG, "received ServiceSpecificException: " + e); 2057 int errorCode = CarPropertyHelper.getVhalSystemErrorCode(e.errorCode); 2058 int vendorErrorCode = CarPropertyHelper.getVhalVendorErrorCode(e.errorCode); 2059 2060 switch (errorCode) { 2061 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE: 2062 throw new PropertyNotAvailableException(propertyId, areaId, vendorErrorCode); 2063 case VehicleHalStatusCode.STATUS_TRY_AGAIN: 2064 // Vendor error code is ignored for STATUS_TRY_AGAIN error 2065 throw new PropertyNotAvailableAndRetryException(propertyId, areaId); 2066 case VehicleHalStatusCode.STATUS_ACCESS_DENIED: 2067 // Vendor error code is ignored for STATUS_ACCESS_DENIED error 2068 throw new PropertyAccessDeniedSecurityException(propertyId, areaId); 2069 case VehicleHalStatusCode.STATUS_INTERNAL_ERROR: 2070 throw new CarInternalErrorException(propertyId, areaId, vendorErrorCode); 2071 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED: 2072 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW: 2073 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH: 2074 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_POOR_VISIBILITY: 2075 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY: 2076 throw new PropertyNotAvailableException(propertyId, areaId, 2077 getPropertyNotAvailableErrorCodeFromStatusCode(errorCode), vendorErrorCode); 2078 default: 2079 Log.e(TAG, "Invalid errorCode: " + errorCode + " in CarService"); 2080 throw new CarInternalErrorException(propertyId, areaId); 2081 } 2082 } 2083 2084 /** 2085 * Convert {@link VehicleHalStatusCode} into public {@link PropertyNotAvailableErrorCode} 2086 * equivalents. 2087 * 2088 * @throws IllegalArgumentException if an invalid status code is passed in. 2089 * @hide 2090 */ getPropertyNotAvailableErrorCodeFromStatusCode(int statusCode)2091 private static int getPropertyNotAvailableErrorCodeFromStatusCode(int statusCode) { 2092 switch (statusCode) { 2093 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE: 2094 return PropertyNotAvailableErrorCode.NOT_AVAILABLE; 2095 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED: 2096 return PropertyNotAvailableErrorCode.NOT_AVAILABLE_DISABLED; 2097 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW: 2098 return PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_LOW; 2099 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH: 2100 return PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_HIGH; 2101 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_POOR_VISIBILITY: 2102 return PropertyNotAvailableErrorCode.NOT_AVAILABLE_POOR_VISIBILITY; 2103 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY: 2104 return PropertyNotAvailableErrorCode.NOT_AVAILABLE_SAFETY; 2105 default: 2106 throw new IllegalArgumentException("Invalid status code: " + statusCode); 2107 } 2108 } 2109 clearRequestIdToAsyncRequestInfo( List<? extends AsyncPropertyRequest> asyncPropertyRequests)2110 private void clearRequestIdToAsyncRequestInfo( 2111 List<? extends AsyncPropertyRequest> asyncPropertyRequests) { 2112 if (DBG) { 2113 Log.d(TAG, "clear pending async requests: " + asyncPropertyRequests); 2114 } 2115 synchronized (mLock) { 2116 for (int i = 0; i < asyncPropertyRequests.size(); i++) { 2117 mRequestIdToAsyncRequestInfo.remove(asyncPropertyRequests.get(i).getRequestId()); 2118 } 2119 } 2120 } 2121 2122 /** 2123 * Set an {@code onCancelListener} for the cancellation signal. 2124 * 2125 * <p>When the signal is cancelled, car service will remove the stored state for the specified 2126 * pending request IDs and ignore all the future results. 2127 */ setOnCancelListener(CancellationSignal cancellationSignal, List<Integer> requestIds)2128 private void setOnCancelListener(CancellationSignal cancellationSignal, 2129 List<Integer> requestIds) { 2130 cancellationSignal.setOnCancelListener(() -> { 2131 int[] requestIdsArray = new int[requestIds.size()]; 2132 synchronized (mLock) { 2133 for (int i = 0; i < requestIds.size(); i++) { 2134 int requestId = requestIds.get(i); 2135 requestIdsArray[i] = requestId; 2136 mRequestIdToAsyncRequestInfo.remove(requestId); 2137 } 2138 } 2139 try { 2140 mService.cancelRequests(requestIdsArray); 2141 } catch (RemoteException e) { 2142 handleRemoteExceptionFromCarService(e); 2143 } 2144 }); 2145 } 2146 2147 /** @hide */ 2148 @Override 2149 @AddedInOrBefore(majorVersion = 33) onCarDisconnected()2150 public void onCarDisconnected() { 2151 synchronized (mLock) { 2152 mPropertyIdToCarPropertyEventCallbackController.clear(); 2153 } 2154 } 2155 2156 /** 2157 * Generate unique get request ID and return to the client. 2158 * 2159 * @param propertyId the property ID 2160 * @param areaId the area ID 2161 * @return the GetPropertyRequest object 2162 */ 2163 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 2164 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 2165 @NonNull 2166 @SuppressWarnings("FormatString") generateGetPropertyRequest(int propertyId, int areaId)2167 public GetPropertyRequest generateGetPropertyRequest(int propertyId, int areaId) { 2168 int requestIdCounter = mRequestIdCounter.getAndIncrement(); 2169 if (DBG) { 2170 Log.d(TAG, String.format("generateGetPropertyRequest, requestId: %d, propertyId: %s, " 2171 + "areaId: %d", requestIdCounter, VehiclePropertyIds.toString(propertyId), 2172 areaId)); 2173 } 2174 return new GetPropertyRequest(requestIdCounter, propertyId, areaId); 2175 } 2176 2177 /** 2178 * Generate unique set request ID and return to the client. 2179 * 2180 * @param <T> the type for the property value, must be one of Object, Boolean, Float, Integer, 2181 * Long, Float[], Integer[], Long[], String, byte[], Object[] 2182 * @param propertyId the property ID 2183 * @param areaId the area ID 2184 * @param value the value to set 2185 * @return the {@link SetPropertyRequest} object 2186 */ 2187 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 2188 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 2189 @NonNull 2190 @SuppressWarnings("FormatString") generateSetPropertyRequest(int propertyId, int areaId, @NonNull T value)2191 public <T> SetPropertyRequest<T> generateSetPropertyRequest(int propertyId, int areaId, 2192 @NonNull T value) { 2193 requireNonNull(value); 2194 int requestIdCounter = mRequestIdCounter.getAndIncrement(); 2195 if (DBG) { 2196 Log.d(TAG, String.format("generateSetPropertyRequest, requestId: %d, propertyId: %s, " 2197 + "areaId: %d, value: %s", requestIdCounter, 2198 VehiclePropertyIds.toString(propertyId), areaId, value)); 2199 } 2200 return new SetPropertyRequest(requestIdCounter, propertyId, areaId, value); 2201 } 2202 checkAsyncArguments(Object requests, Object callback, long timeoutInMs)2203 private void checkAsyncArguments(Object requests, Object callback, long timeoutInMs) { 2204 requireNonNull(requests); 2205 requireNonNull(callback); 2206 if (timeoutInMs <= 0) { 2207 throw new IllegalArgumentException("timeoutInMs must be a positive number"); 2208 } 2209 } 2210 2211 /** 2212 * Query a list of {@link CarPropertyValue} with property ID and area ID asynchronously. 2213 * 2214 * <p>This function would return immediately before the results are ready. For each request, 2215 * the corresponding result would either be delivered through one 2216 * {@code resultCallback.onSuccess} call if the request succeeded or through one 2217 * {@code errorCallback.onFailure} call if failed. It is guaranteed that the total times the 2218 * callback functions are called is equal to the number of requests if this function does not 2219 * throw an exception. It is guaranteed that none of the callback functions are called if an 2220 * exception is thrown. If the {@code callbackExecutor} is {@code null}, the callback will be 2221 * executed on the default event handler thread. If the callback is doing heavy work, it is 2222 * recommended that the {@code callbackExecutor} is provided. 2223 * 2224 * <p>If the operation is cancelled, it is guaranteed that no more callbacks will be called. 2225 * 2226 * <p>For one request, if the property's status is not available, 2227 * {@code errorCallback.onFailure} will be called once with {@link #STATUS_ERROR_NOT_AVAILABLE}. 2228 * 2229 * <p>For one request, if the property's status is error, 2230 * {@code errorCallback.onFailure} will be called once with {@link 2231 * #STATUS_ERROR_INTERNAL_ERROR}. 2232 * 2233 * @param getPropertyRequests a list of properties to get 2234 * @param timeoutInMs the timeout for the operation, in milliseconds 2235 * @param cancellationSignal a signal that could be used to cancel the on-going operation 2236 * @param callbackExecutor the executor to execute the callback with 2237 * @param getPropertyCallback the callback function to deliver the result 2238 * @throws SecurityException if missing permission to read one of the specific properties. 2239 * @throws IllegalArgumentException if one of the properties to read is not supported. 2240 */ 2241 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 2242 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getPropertiesAsync( @onNull List<GetPropertyRequest> getPropertyRequests, long timeoutInMs, @Nullable CancellationSignal cancellationSignal, @Nullable Executor callbackExecutor, @NonNull GetPropertyCallback getPropertyCallback)2243 public void getPropertiesAsync( 2244 @NonNull List<GetPropertyRequest> getPropertyRequests, 2245 long timeoutInMs, 2246 @Nullable CancellationSignal cancellationSignal, 2247 @Nullable Executor callbackExecutor, 2248 @NonNull GetPropertyCallback getPropertyCallback) { 2249 if (DBG) { 2250 Log.d(TAG, "getPropertiesAsync, requests: " + getPropertyRequests + ", timeoutInMs: " 2251 + timeoutInMs + ", callback: " + getPropertyCallback); 2252 } 2253 2254 checkAsyncArguments(getPropertyRequests, getPropertyCallback, timeoutInMs); 2255 if (callbackExecutor == null) { 2256 callbackExecutor = new HandlerExecutor(getEventHandler()); 2257 } 2258 2259 List<AsyncPropertyServiceRequest> getPropertyServiceRequests = new ArrayList<>( 2260 getPropertyRequests.size()); 2261 for (int i = 0; i < getPropertyRequests.size(); i++) { 2262 GetPropertyRequest getPropertyRequest = getPropertyRequests.get(i); 2263 int propertyId = getPropertyRequest.getPropertyId(); 2264 int areaId = getPropertyRequest.getAreaId(); 2265 assertPropertyIdIsSupported(propertyId); 2266 2267 getPropertyServiceRequests.add(AsyncPropertyServiceRequest.newGetAsyncRequest( 2268 getPropertyRequest)); 2269 } 2270 2271 List<Integer> requestIds = storePendingRequestInfo(getPropertyRequests, callbackExecutor, 2272 getPropertyCallback); 2273 2274 try { 2275 if (DBG) { 2276 Log.d(TAG, "calling CarPropertyService.getPropertiesAsync"); 2277 } 2278 mService.getPropertiesAsync(new AsyncPropertyServiceRequestList( 2279 getPropertyServiceRequests), mAsyncPropertyResultCallback, timeoutInMs); 2280 if (DBG) { 2281 Log.d(TAG, "CarPropertyService.getPropertiesAsync succeed"); 2282 } 2283 } catch (RemoteException e) { 2284 clearRequestIdToAsyncRequestInfo(getPropertyRequests); 2285 handleRemoteExceptionFromCarService(e); 2286 } catch (IllegalArgumentException | SecurityException e) { 2287 clearRequestIdToAsyncRequestInfo(getPropertyRequests); 2288 throw e; 2289 } 2290 if (cancellationSignal != null) { 2291 setOnCancelListener(cancellationSignal, requestIds); 2292 } 2293 } 2294 2295 /** 2296 * Query a list of {@link CarPropertyValue} with property Id and area Id asynchronously. 2297 * 2298 * Same as {@link CarPropertyManager#getPropertiesAsync(List, long, CancellationSignal, 2299 * Executor, GetPropertyCallback)} with default timeout 10s. 2300 */ 2301 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 2302 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getPropertiesAsync( @onNull List<GetPropertyRequest> getPropertyRequests, @Nullable CancellationSignal cancellationSignal, @Nullable Executor callbackExecutor, @NonNull GetPropertyCallback getPropertyCallback)2303 public void getPropertiesAsync( 2304 @NonNull List<GetPropertyRequest> getPropertyRequests, 2305 @Nullable CancellationSignal cancellationSignal, 2306 @Nullable Executor callbackExecutor, 2307 @NonNull GetPropertyCallback getPropertyCallback) { 2308 getPropertiesAsync(getPropertyRequests, ASYNC_GET_DEFAULT_TIMEOUT_MS, cancellationSignal, 2309 callbackExecutor, getPropertyCallback); 2310 } 2311 2312 /** 2313 * Sets a list of car property values asynchronously. 2314 * 2315 * <p>This function would return immediately before the results are ready. For each request, 2316 * the corresponding result would either be delivered through one 2317 * {@code resultCallback.onSuccess} call if the request succeeded or through one 2318 * {@code errorCallback.onFailure} call if failed. It is guaranteed that the total times the 2319 * callback functions are called is equal to the number of requests if this function does not 2320 * throw an exception. It is guaranteed that none of the callback functions are called if an 2321 * exception is thrown. If the {@code callbackExecutor} is {@code null}, the callback will be 2322 * executed on the default event handler thread. If the callback is doing heavy work, it is 2323 * recommended that the {@code callbackExecutor} is provided. 2324 * 2325 * <p>If the operation is cancelled, it is guaranteed that no more callbacks will be called. 2326 * 2327 * <p>If multiple clients set a property for the same area ID simultaneously, which one takes 2328 * precedence is undefined. Typically, the last set operation (in the order that they are issued 2329 * to the car's ECU) overrides the previous set operations. 2330 * 2331 * <p>When the success callback will be called depends on whether {@code waitForPropertyUpdate} 2332 * for each request is set. If this is set to {@code true} (by default), the success callback 2333 * will be called when the set operation is successfully delivered to vehicle bus AND either 2334 * target value is the same as the current or when the property is updated to the target value. 2335 * 2336 * <p>When {@code waitForPropertyUpdate} is set to {@code false}, the success callback will be 2337 * called as long as the set operation is successfully delivered to vehicle bus. 2338 * 2339 * <p>Under most cases, client should wait for the property update to verify that the set 2340 * operation actually succeeded. 2341 * 2342 * <p>For cases when the property is write-only (no way to get property update event) or when 2343 * the property represents some action, instead of an actual state, e.g. key stroke where the 2344 * property's current value is not meaningful, caller must set {@code waitForPropertyUpdate} 2345 * to {@code false}. 2346 * 2347 * <p>For {@code HVAC_TEMPERATURE_VALUE_SUGGESTION}, this must be set to {@code false} 2348 * because the updated property value will not be the same as the value to be set. 2349 * 2350 * @param setPropertyRequests a list of properties to set 2351 * @param timeoutInMs the timeout for the operation, in milliseconds 2352 * @param cancellationSignal a signal that could be used to cancel the on-going operation 2353 * @param callbackExecutor the executor to execute the callback with 2354 * @param setPropertyCallback the callback function to deliver the result 2355 * @throws SecurityException if missing permission to write one of the specific properties. 2356 * @throws IllegalArgumentException if one of the properties to set is not supported. 2357 * @throws IllegalArgumentException if one of the properties is not readable and does not set 2358 * {@code waitForPropertyUpdate} to {@code false}. 2359 * @throws IllegalArgumentException if one of the properties is 2360 * {@code HVAC_TEMPERATURE_VALUE_SUGGESTION} and does not set {@code waitForPropertyUpdate} 2361 * to {@code false}. 2362 */ 2363 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 2364 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setPropertiesAsync( @onNull List<SetPropertyRequest<?>> setPropertyRequests, long timeoutInMs, @Nullable CancellationSignal cancellationSignal, @Nullable Executor callbackExecutor, @NonNull SetPropertyCallback setPropertyCallback)2365 public void setPropertiesAsync( 2366 @NonNull List<SetPropertyRequest<?>> setPropertyRequests, 2367 long timeoutInMs, 2368 @Nullable CancellationSignal cancellationSignal, 2369 @Nullable Executor callbackExecutor, 2370 @NonNull SetPropertyCallback setPropertyCallback) { 2371 if (DBG) { 2372 Log.d(TAG, "setPropertiesAsync, requests: " + setPropertyRequests + ", timeoutInMs: " 2373 + timeoutInMs + ", callback: " + setPropertyCallback); 2374 } 2375 2376 checkAsyncArguments(setPropertyRequests, setPropertyCallback, timeoutInMs); 2377 if (callbackExecutor == null) { 2378 callbackExecutor = new HandlerExecutor(getEventHandler()); 2379 } 2380 2381 List<AsyncPropertyServiceRequest> setPropertyServiceRequests = new ArrayList<>( 2382 setPropertyRequests.size()); 2383 for (int i = 0; i < setPropertyRequests.size(); i++) { 2384 SetPropertyRequest setPropertyRequest = setPropertyRequests.get(i); 2385 int propertyId = setPropertyRequest.getPropertyId(); 2386 int areaId = setPropertyRequest.getAreaId(); 2387 requireNonNull(setPropertyRequest.getValue()); 2388 assertPropertyIdIsSupported(propertyId); 2389 2390 setPropertyServiceRequests.add(AsyncPropertyServiceRequest.newSetAsyncRequest( 2391 setPropertyRequest)); 2392 } 2393 2394 List<Integer> requestIds = storePendingRequestInfo(setPropertyRequests, callbackExecutor, 2395 setPropertyCallback); 2396 2397 try { 2398 if (DBG) { 2399 Log.d(TAG, "calling CarPropertyService.setPropertiesAsync"); 2400 } 2401 mService.setPropertiesAsync(new AsyncPropertyServiceRequestList( 2402 setPropertyServiceRequests), mAsyncPropertyResultCallback, timeoutInMs); 2403 if (DBG) { 2404 Log.d(TAG, "CarPropertyService.setPropertiesAsync succeed"); 2405 } 2406 } catch (RemoteException e) { 2407 clearRequestIdToAsyncRequestInfo(setPropertyRequests); 2408 handleRemoteExceptionFromCarService(e); 2409 } catch (IllegalArgumentException | SecurityException e) { 2410 clearRequestIdToAsyncRequestInfo(setPropertyRequests); 2411 throw e; 2412 } 2413 if (cancellationSignal != null) { 2414 setOnCancelListener(cancellationSignal, requestIds); 2415 } 2416 } 2417 2418 /** 2419 * Sets a list of car property values asynchronously. 2420 * 2421 * Same as {@link CarPropertyManager#setPropertiesAsync(List, long, CancellationSignal, 2422 * Executor, SetPropertyCallback)} with default timeout 10s. 2423 */ 2424 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 2425 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setPropertiesAsync( @onNull List<SetPropertyRequest<?>> setPropertyRequests, @Nullable CancellationSignal cancellationSignal, @Nullable Executor callbackExecutor, @NonNull SetPropertyCallback setPropertyCallback)2426 public void setPropertiesAsync( 2427 @NonNull List<SetPropertyRequest<?>> setPropertyRequests, 2428 @Nullable CancellationSignal cancellationSignal, 2429 @Nullable Executor callbackExecutor, 2430 @NonNull SetPropertyCallback setPropertyCallback) { 2431 setPropertiesAsync(setPropertyRequests, ASYNC_GET_DEFAULT_TIMEOUT_MS, cancellationSignal, 2432 callbackExecutor, setPropertyCallback); 2433 } 2434 assertPropertyIdIsSupported(int propertyId)2435 private void assertPropertyIdIsSupported(int propertyId) { 2436 if (!CarPropertyHelper.isSupported(propertyId)) { 2437 throw new IllegalArgumentException("The property: " 2438 + VehiclePropertyIds.toString(propertyId) + " is unsupported"); 2439 } 2440 } 2441 2442 private <RequestType extends AsyncPropertyRequest, CallbackType> List<Integer> storePendingRequestInfo( List<RequestType> requests, Executor callbackExecutor, CallbackType callback)2443 storePendingRequestInfo( 2444 List<RequestType> requests, Executor callbackExecutor, CallbackType callback) { 2445 if (DBG) { 2446 Log.d(TAG, "store pending async requests: " + requests); 2447 } 2448 List<Integer> requestIds = new ArrayList<>(); 2449 SparseArray<AsyncPropertyRequestInfo<?, ?>> requestInfoToAdd = new SparseArray<>(); 2450 synchronized (mLock) { 2451 for (int i = 0; i < requests.size(); i++) { 2452 RequestType request = requests.get(i); 2453 AsyncPropertyRequestInfo<RequestType, CallbackType> requestInfo = 2454 new AsyncPropertyRequestInfo(request, callbackExecutor, callback); 2455 int requestId = request.getRequestId(); 2456 requestIds.add(requestId); 2457 if (mRequestIdToAsyncRequestInfo.contains(requestId) 2458 || requestInfoToAdd.contains(requestId)) { 2459 throw new IllegalArgumentException( 2460 "Request ID: " + requestId + " already exists"); 2461 } 2462 requestInfoToAdd.put(requestId, requestInfo); 2463 } 2464 for (int i = 0; i < requestInfoToAdd.size(); i++) { 2465 mRequestIdToAsyncRequestInfo.put(requestInfoToAdd.keyAt(i), 2466 requestInfoToAdd.valueAt(i)); 2467 } 2468 } 2469 return requestIds; 2470 } 2471 } 2472