• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.property.CarPropertyHelper.SYNC_OP_LIMIT_TRY_AGAIN;
20 
21 import static java.lang.Integer.toHexString;
22 import static java.util.Objects.requireNonNull;
23 
24 import android.annotation.FloatRange;
25 import android.annotation.IntDef;
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.car.Car;
29 import android.car.CarManagerBase;
30 import android.car.VehicleAreaType;
31 import android.car.VehiclePropertyIds;
32 import android.car.annotation.AddedInOrBefore;
33 import android.car.hardware.CarPropertyConfig;
34 import android.car.hardware.CarPropertyValue;
35 import android.os.Build;
36 import android.os.Handler;
37 import android.os.RemoteException;
38 import android.os.ServiceSpecificException;
39 import android.os.SystemClock;
40 import android.util.ArraySet;
41 import android.util.Log;
42 import android.util.SparseArray;
43 
44 import com.android.car.internal.CarPropertyEventCallbackController;
45 import com.android.car.internal.SingleMessageHandler;
46 import com.android.internal.annotations.GuardedBy;
47 
48 import java.lang.annotation.Retention;
49 import java.lang.annotation.RetentionPolicy;
50 import java.lang.ref.WeakReference;
51 import java.util.ArrayList;
52 import java.util.List;
53 import java.util.concurrent.Callable;
54 
55 /**
56  * Provides an application interface for interacting with the Vehicle specific properties.
57  * For details about the individual properties, see the descriptions in
58  * hardware/interfaces/automotive/vehicle/types.hal
59  */
60 public class CarPropertyManager extends CarManagerBase {
61     private static final boolean DBG = false;
62     private static final String TAG = "CarPropertyManager";
63     private static final int MSG_GENERIC_EVENT = 0;
64     private static final int SYNC_OP_RETRY_SLEEP_IN_MS = 10;
65     private static final int SYNC_OP_RETRY_MAX_COUNT = 10;
66 
67     private final SingleMessageHandler<CarPropertyEvent> mHandler;
68     private final ICarProperty mService;
69     private final int mAppTargetSdk;
70 
71     private final CarPropertyEventListenerToService mCarPropertyEventToService =
72             new CarPropertyEventListenerToService(this);
73 
74     // This lock is shared with all CarPropertyEventCallbackController instances to prevent
75     // potential deadlock.
76     private final Object mLock = new Object();
77     @GuardedBy("mLock")
78     private final SparseArray<CarPropertyEventCallbackController>
79             mPropertyIdToCarPropertyEventCallbackController = new SparseArray<>();
80 
81     private final CarPropertyEventCallbackController.RegistrationUpdateCallback
82             mRegistrationUpdateCallback =
83             new CarPropertyEventCallbackController.RegistrationUpdateCallback() {
84                 @Override
85                 public boolean register(int propertyId, float updateRateHz) {
86                     try {
87                         mService.registerListener(propertyId, updateRateHz,
88                                 mCarPropertyEventToService);
89                     } catch (RemoteException e) {
90                         handleRemoteExceptionFromCarService(e);
91                         return false;
92                     }
93                     return true;
94                 }
95 
96                 @Override
97                 public void unregister(int propertyId) {
98                     try {
99                         mService.unregisterListener(propertyId, mCarPropertyEventToService);
100                     } catch (RemoteException e) {
101                         handleRemoteExceptionFromCarService(e);
102                     }
103                 }
104             };
105 
106     /**
107      * Application registers {@link CarPropertyEventCallback} object to receive updates and changes
108      * to subscribed Vehicle specific properties.
109      */
110     public interface CarPropertyEventCallback {
111         /**
112          * Called when a property is updated
113          * @param value Property that has been updated.
114          */
115         @AddedInOrBefore(majorVersion = 33)
onChangeEvent(CarPropertyValue value)116         void onChangeEvent(CarPropertyValue value);
117 
118         /**
119          * Called when an error is detected when setting a property.
120          *
121          * @param propId Property ID which is detected an error.
122          * @param zone Zone which is detected an error.
123          *
124          * @see CarPropertyEventCallback#onErrorEvent(int, int, int)
125          */
126         @AddedInOrBefore(majorVersion = 33)
onErrorEvent(int propId, int zone)127         void onErrorEvent(int propId, int zone);
128 
129         /**
130          * Called when an error is detected when setting a property.
131          *
132          * <p>Clients which changed the property value in the areaId most recently will receive
133          * this callback. If multiple clients set a property for the same area id simultaneously,
134          * which one takes precedence is undefined. Typically, the last set operation
135          * (in the order that they are issued to car's ECU) overrides the previous set operations.
136          * The delivered error reflects the error happened in the last set operation.
137          *
138          * @param propId Property ID which is detected an error.
139          * @param areaId AreaId which is detected an error.
140          * @param errorCode Error code is raised in the car.
141          */
142         @AddedInOrBefore(majorVersion = 33)
onErrorEvent(int propId, int areaId, @CarSetPropertyErrorCode int errorCode)143         default void onErrorEvent(int propId, int areaId, @CarSetPropertyErrorCode int errorCode) {
144             if (DBG) {
145                 Log.d(TAG, "onErrorEvent propertyId: 0x" + toHexString(propId) + " areaId:0x"
146                         + toHexString(areaId) + " ErrorCode: " + errorCode);
147             }
148             onErrorEvent(propId, areaId);
149         }
150     }
151 
152     /** Read ONCHANGE sensors. */
153     @AddedInOrBefore(majorVersion = 33)
154     public static final float SENSOR_RATE_ONCHANGE = 0f;
155     /** Read sensors at the rate of  1 hertz */
156     @AddedInOrBefore(majorVersion = 33)
157     public static final float SENSOR_RATE_NORMAL = 1f;
158     /** Read sensors at the rate of 5 hertz */
159     @AddedInOrBefore(majorVersion = 33)
160     public static final float SENSOR_RATE_UI = 5f;
161     /** Read sensors at the rate of 10 hertz */
162     @AddedInOrBefore(majorVersion = 33)
163     public static final float SENSOR_RATE_FAST = 10f;
164     /** Read sensors at the rate of 100 hertz */
165     @AddedInOrBefore(majorVersion = 33)
166     public static final float SENSOR_RATE_FASTEST = 100f;
167 
168 
169 
170     /**
171      * Status to indicate that set operation failed. Try it again.
172      */
173     @AddedInOrBefore(majorVersion = 33)
174     public static final int CAR_SET_PROPERTY_ERROR_CODE_TRY_AGAIN = 1;
175 
176     /**
177      * Status to indicate that set operation failed because of an invalid argument.
178      */
179     @AddedInOrBefore(majorVersion = 33)
180     public static final int CAR_SET_PROPERTY_ERROR_CODE_INVALID_ARG = 2;
181 
182     /**
183      * Status to indicate that set operation failed because the property is not available.
184      */
185     @AddedInOrBefore(majorVersion = 33)
186     public static final int CAR_SET_PROPERTY_ERROR_CODE_PROPERTY_NOT_AVAILABLE = 3;
187 
188     /**
189      * Status to indicate that set operation failed because car denied access to the property.
190      */
191     @AddedInOrBefore(majorVersion = 33)
192     public static final int CAR_SET_PROPERTY_ERROR_CODE_ACCESS_DENIED = 4;
193 
194     /**
195      * Status to indicate that set operation failed because of an general error in cars.
196      */
197     @AddedInOrBefore(majorVersion = 33)
198     public static final int CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN = 5;
199 
200     /** @hide */
201     @IntDef(prefix = {"CAR_SET_PROPERTY_ERROR_CODE_"}, value = {
202             CAR_SET_PROPERTY_ERROR_CODE_TRY_AGAIN,
203             CAR_SET_PROPERTY_ERROR_CODE_INVALID_ARG,
204             CAR_SET_PROPERTY_ERROR_CODE_PROPERTY_NOT_AVAILABLE,
205             CAR_SET_PROPERTY_ERROR_CODE_ACCESS_DENIED,
206             CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN,
207     })
208     @Retention(RetentionPolicy.SOURCE)
209     public @interface CarSetPropertyErrorCode {}
210 
211     /**
212      * Get an instance of the CarPropertyManager.
213      *
214      * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
215      * @param car Car instance
216      * @param service ICarProperty instance
217      * @hide
218      */
CarPropertyManager(Car car, @NonNull ICarProperty service)219     public CarPropertyManager(Car car, @NonNull ICarProperty service) {
220         super(car);
221         mService = service;
222         mAppTargetSdk = getContext().getApplicationInfo().targetSdkVersion;
223 
224         Handler eventHandler = getEventHandler();
225         if (eventHandler == null) {
226             mHandler = null;
227             return;
228         }
229         mHandler = new SingleMessageHandler<CarPropertyEvent>(eventHandler.getLooper(),
230             MSG_GENERIC_EVENT) {
231             @Override
232             protected void handleEvent(CarPropertyEvent carPropertyEvent) {
233                 CarPropertyEventCallbackController carPropertyEventCallbackController;
234                 synchronized (mLock) {
235                     carPropertyEventCallbackController =
236                             mPropertyIdToCarPropertyEventCallbackController.get(
237                                     carPropertyEvent.getCarPropertyValue().getPropertyId());
238                 }
239                 if (carPropertyEventCallbackController == null) {
240                     return;
241                 }
242                 switch (carPropertyEvent.getEventType()) {
243                     case CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE:
244                         carPropertyEventCallbackController.forwardPropertyChanged(carPropertyEvent);
245                         break;
246                     case CarPropertyEvent.PROPERTY_EVENT_ERROR:
247                         carPropertyEventCallbackController.forwardErrorEvent(carPropertyEvent);
248                         break;
249                     default:
250                         throw new IllegalArgumentException();
251                 }
252             }
253         };
254     }
255 
256     /**
257      * Register {@link CarPropertyEventCallback} to get property updates. Multiple callbacks
258      * can be registered for a single property or the same callback can be used for different
259      * properties. If the same callback is registered again for the same property, it will be
260      * updated to new updateRateHz.
261      * <p>Rate could be one of the following:
262      * <ul>
263      *   <li>{@link CarPropertyManager#SENSOR_RATE_ONCHANGE}</li>
264      *   <li>{@link CarPropertyManager#SENSOR_RATE_NORMAL}</li>
265      *   <li>{@link CarPropertyManager#SENSOR_RATE_UI}</li>
266      *   <li>{@link CarPropertyManager#SENSOR_RATE_FAST}</li>
267      *   <li>{@link CarPropertyManager#SENSOR_RATE_FASTEST}</li>
268      * </ul>
269      * <p>
270      * <b>Note:</b>Rate has no effect if the property has one of the following change modes:
271      * <ul>
272      *   <li>{@link CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_STATIC}</li>
273      *   <li>{@link CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE}</li>
274      * </ul>
275      * <b>Note:</b>If listener registers for updates for a
276      * {@link CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE} property, it will receive the
277      * property's current value upon registration.
278      * See {@link CarPropertyConfig#getChangeMode()} for details.
279      * If updateRateHz is higher than {@link CarPropertyConfig#getMaxSampleRate()}, it will be
280      * registered with max sample updateRateHz.
281      * If updateRateHz is lower than {@link CarPropertyConfig#getMinSampleRate()}, it will be
282      * registered with min sample updateRateHz.
283      *
284      * @param carPropertyEventCallback CarPropertyEventCallback to be registered.
285      * @param propertyId               PropertyId to subscribe
286      * @param updateRateHz             how fast the property events are delivered in Hz.
287      * @return true if the listener is successfully registered.
288      * @throws SecurityException if missing the appropriate permission.
289      */
290     @AddedInOrBefore(majorVersion = 33)
registerCallback(@onNull CarPropertyEventCallback carPropertyEventCallback, int propertyId, @FloatRange(from = 0.0, to = 100.0) float updateRateHz)291     public boolean registerCallback(@NonNull CarPropertyEventCallback carPropertyEventCallback,
292             int propertyId, @FloatRange(from = 0.0, to = 100.0) float updateRateHz) {
293         requireNonNull(carPropertyEventCallback);
294         CarPropertyConfig<?> carPropertyConfig = getCarPropertyConfig(propertyId);
295         if (carPropertyConfig == null) {
296             Log.e(TAG, "registerListener:  propId is not in carPropertyConfig list:  "
297                     + VehiclePropertyIds.toString(propertyId));
298             return false;
299         }
300         if (carPropertyConfig.getChangeMode()
301                 != CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS) {
302             updateRateHz = SENSOR_RATE_ONCHANGE;
303         } else if (updateRateHz > carPropertyConfig.getMaxSampleRate()) {
304             updateRateHz = carPropertyConfig.getMaxSampleRate();
305         } else if (updateRateHz < carPropertyConfig.getMinSampleRate()) {
306             updateRateHz = carPropertyConfig.getMinSampleRate();
307         }
308         CarPropertyEventCallbackController carPropertyEventCallbackController;
309         synchronized (mLock) {
310             carPropertyEventCallbackController =
311                     mPropertyIdToCarPropertyEventCallbackController.get(propertyId);
312             if (carPropertyEventCallbackController == null) {
313                 carPropertyEventCallbackController = new CarPropertyEventCallbackController(
314                         propertyId, mLock, mRegistrationUpdateCallback);
315                 mPropertyIdToCarPropertyEventCallbackController.put(propertyId,
316                         carPropertyEventCallbackController);
317             }
318         }
319         return carPropertyEventCallbackController.add(carPropertyEventCallback, updateRateHz);
320     }
321 
322     private static class CarPropertyEventListenerToService extends ICarPropertyEventListener.Stub {
323         private final WeakReference<CarPropertyManager> mCarPropertyManager;
324 
CarPropertyEventListenerToService(CarPropertyManager carPropertyManager)325         CarPropertyEventListenerToService(CarPropertyManager carPropertyManager) {
326             mCarPropertyManager = new WeakReference<>(carPropertyManager);
327         }
328 
329         @Override
onEvent(List<CarPropertyEvent> carPropertyEvents)330         public void onEvent(List<CarPropertyEvent> carPropertyEvents) throws RemoteException {
331             CarPropertyManager carPropertyManager = mCarPropertyManager.get();
332             if (carPropertyManager != null) {
333                 carPropertyManager.handleEvents(carPropertyEvents);
334             }
335         }
336     }
337 
handleEvents(List<CarPropertyEvent> carPropertyEvents)338     private void handleEvents(List<CarPropertyEvent> carPropertyEvents) {
339         if (mHandler != null) {
340             mHandler.sendEvents(carPropertyEvents);
341         }
342     }
343 
344     /**
345      * Stop getting property updates for the given {@link CarPropertyEventCallback}. If there are
346      * multiple registrations for this {@link CarPropertyEventCallback}, all listening will be
347      * stopped.
348      */
349     @AddedInOrBefore(majorVersion = 33)
unregisterCallback(@onNull CarPropertyEventCallback carPropertyEventCallback)350     public void unregisterCallback(@NonNull CarPropertyEventCallback carPropertyEventCallback) {
351         requireNonNull(carPropertyEventCallback);
352         int[] propertyIds;
353         synchronized (mLock) {
354             propertyIds = new int[mPropertyIdToCarPropertyEventCallbackController.size()];
355             for (int i = 0; i < mPropertyIdToCarPropertyEventCallbackController.size(); i++) {
356                 propertyIds[i] = mPropertyIdToCarPropertyEventCallbackController.keyAt(i);
357             }
358         }
359         for (int propertyId : propertyIds) {
360             unregisterCallback(carPropertyEventCallback, propertyId);
361         }
362     }
363 
364     /**
365      * Stop getting update for {@code propertyId} to the given {@link CarPropertyEventCallback}. If
366      * the same {@link CarPropertyEventCallback} is used for other properties, those subscriptions
367      * will not be affected.
368      */
369     @AddedInOrBefore(majorVersion = 33)
unregisterCallback(@onNull CarPropertyEventCallback carPropertyEventCallback, int propertyId)370     public void unregisterCallback(@NonNull CarPropertyEventCallback carPropertyEventCallback,
371             int propertyId) {
372         requireNonNull(carPropertyEventCallback);
373         CarPropertyEventCallbackController carPropertyEventCallbackController;
374         synchronized (mLock) {
375             carPropertyEventCallbackController =
376                     mPropertyIdToCarPropertyEventCallbackController.get(propertyId);
377         }
378         if (carPropertyEventCallbackController == null) {
379             return;
380         }
381         synchronized (mLock) {
382             boolean allCallbacksRemoved = carPropertyEventCallbackController.remove(
383                     carPropertyEventCallback);
384             if (allCallbacksRemoved) {
385                 mPropertyIdToCarPropertyEventCallbackController.remove(propertyId);
386             }
387         }
388     }
389 
390     /**
391      * @return List of properties implemented by this car that the application may access.
392      */
393     @NonNull
394     @AddedInOrBefore(majorVersion = 33)
getPropertyList()395     public List<CarPropertyConfig> getPropertyList() {
396         List<CarPropertyConfig> configs;
397         try {
398             configs = mService.getPropertyList();
399         } catch (RemoteException e) {
400             Log.e(TAG, "getPropertyList exception ", e);
401             return handleRemoteExceptionFromCarService(e, new ArrayList<CarPropertyConfig>());
402         }
403         return configs;
404     }
405 
406     /**
407      * @param propertyIds property ID list
408      * @return List of properties implemented by this car in given property ID list that application
409      *          may access.
410      */
411     @NonNull
412     @AddedInOrBefore(majorVersion = 33)
getPropertyList(@onNull ArraySet<Integer> propertyIds)413     public List<CarPropertyConfig> getPropertyList(@NonNull ArraySet<Integer> propertyIds) {
414         int[] propIds = new int[propertyIds.size()];
415         int idx = 0;
416         for (int propId : propertyIds) {
417             checkSupportedProperty(propId);
418             propIds[idx++] = propId;
419         }
420         List<CarPropertyConfig> configs;
421         try {
422             configs = mService.getPropertyConfigList(propIds);
423         } catch (RemoteException e) {
424             Log.e(TAG, "getPropertyList exception ", e);
425             return handleRemoteExceptionFromCarService(e, new ArrayList<CarPropertyConfig>());
426         }
427         return configs;
428     }
429 
430     /**
431      * Get CarPropertyConfig by property Id.
432      *
433      * @param propId Property ID
434      * @return {@link CarPropertyConfig} for the selected property.
435      * Null if the property is not available.
436      */
437     @Nullable
438     @AddedInOrBefore(majorVersion = 33)
getCarPropertyConfig(int propId)439     public CarPropertyConfig<?> getCarPropertyConfig(int propId) {
440         checkSupportedProperty(propId);
441         List<CarPropertyConfig> configs;
442         try {
443             configs = mService.getPropertyConfigList(new int[] {propId});
444         } catch (RemoteException e) {
445             Log.e(TAG, "getPropertyList exception ", e);
446             return handleRemoteExceptionFromCarService(e, null);
447         }
448         return configs.size() == 0 ? null : configs.get(0);
449     }
450 
451     /**
452      * Returns areaId contains the seletcted area for the property.
453      *
454      * @param propId Property ID
455      * @param area Area enum such as Enums in {@link android.car.VehicleAreaSeat}.
456      * @throws IllegalArgumentException if the property is not available in the vehicle for
457      * the selected area.
458      * @return AreaId contains the selected area for the property.
459      */
460     @AddedInOrBefore(majorVersion = 33)
getAreaId(int propId, int area)461     public int getAreaId(int propId, int area) {
462         checkSupportedProperty(propId);
463 
464         CarPropertyConfig<?> propConfig = getCarPropertyConfig(propId);
465         if (propConfig == null) {
466             throw new IllegalArgumentException("The property propId: 0x" + toHexString(propId)
467                     + " is not available");
468         }
469         // For the global property, areaId is 0
470         if (propConfig.isGlobalProperty()) {
471             return VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
472         }
473         for (int areaId : propConfig.getAreaIds()) {
474             if ((area & areaId) == area) {
475                 return areaId;
476             }
477         }
478 
479         throw new IllegalArgumentException("The property propId: 0x" + toHexString(propId)
480                 + " is not available at the area: 0x" + toHexString(area));
481     }
482 
483     /**
484      * Return read permission string for given property ID.
485      *
486      * @param propId Property ID to query
487      * @return String Permission needed to read this property.  NULL if propId not available.
488      * @hide
489      */
490     @Nullable
491     @AddedInOrBefore(majorVersion = 33)
getReadPermission(int propId)492     public String getReadPermission(int propId) {
493         if (DBG) {
494             Log.d(TAG, "getReadPermission, propId: 0x" + toHexString(propId));
495         }
496         checkSupportedProperty(propId);
497         try {
498             return mService.getReadPermission(propId);
499         } catch (RemoteException e) {
500             return handleRemoteExceptionFromCarService(e, "");
501         }
502     }
503 
504     /**
505      * Return write permission string for given property ID.
506      *
507      * @param propId Property ID to query
508      * @return String Permission needed to write this property.  NULL if propId not available.
509      * @hide
510      */
511     @Nullable
512     @AddedInOrBefore(majorVersion = 33)
getWritePermission(int propId)513     public String getWritePermission(int propId) {
514         if (DBG) {
515             Log.d(TAG, "getWritePermission, propId: 0x" + toHexString(propId));
516         }
517         checkSupportedProperty(propId);
518         try {
519             return mService.getWritePermission(propId);
520         } catch (RemoteException e) {
521             return handleRemoteExceptionFromCarService(e, "");
522         }
523     }
524 
525 
526     /**
527      * Check whether a given property is available or disabled based on the car's current state.
528      * @param propId Property Id
529      * @param area AreaId of property
530      * @return true if STATUS_AVAILABLE, false otherwise (eg STATUS_UNAVAILABLE)
531      */
532     @AddedInOrBefore(majorVersion = 33)
isPropertyAvailable(int propId, int area)533     public boolean isPropertyAvailable(int propId, int area) {
534         checkSupportedProperty(propId);
535         try {
536             CarPropertyValue propValue = runSyncOperation(() -> {
537                 return mService.getProperty(propId, area);
538             });
539             return (propValue != null)
540                     && (propValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE);
541         } catch (RemoteException e) {
542             return handleRemoteExceptionFromCarService(e, false);
543         } catch (ServiceSpecificException e) {
544             Log.e(TAG, "unable to get property, error: " + e);
545             return false;
546         }
547     }
548 
549     /**
550      * Returns value of a bool property
551      *
552      * <p> This method may take couple seconds to complete, so it needs to be called from an
553      * non-main thread.
554      *
555      * <p> Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} equal
556      * or later than {@link Build.VERSION_CODES#R} will receive the following exceptions when
557      * request is failed.
558      * <ul>
559      *     <li>{@link CarInternalErrorException}
560      *     <li>{@link PropertyAccessDeniedSecurityException}
561      *     <li>{@link PropertyNotAvailableAndRetryException}
562      *     <li>{@link PropertyNotAvailableException}
563      *     <li>{@link IllegalArgumentException}
564      * </ul>
565      * <p> Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion}
566      * earlier than {@link Build.VERSION_CODES#R} will receive the following exceptions if the call
567      * fails.
568      * <ul>
569      *     <li>{@link IllegalStateException} when there is an error detected in cars.
570      *     <li>{@link IllegalArgumentException} when the property in the areaId is not supplied.
571      * </ul>
572      *
573      * @param prop Property ID to get
574      * @param area Area of the property to get
575      *
576      * @throws {@link CarInternalErrorException} when there is an error detected in cars.
577      * @throws {@link PropertyAccessDeniedSecurityException} when cars denied the access of the
578      * property.
579      * @throws {@link PropertyNotAvailableAndRetryException} when the property is temporarily
580      * not available and likely that retrying will be successful.
581      * @throws {@link PropertyNotAvailableException} when the property is temporarily not available.
582      * @throws {@link IllegalArgumentException} when the property in the areaId is not supplied.
583      *
584      * @return value of a bool property, {@code false} if can not get value from cars.
585      */
586     @AddedInOrBefore(majorVersion = 33)
getBooleanProperty(int prop, int area)587     public boolean getBooleanProperty(int prop, int area) {
588         checkSupportedProperty(prop);
589         CarPropertyValue<Boolean> carProp = getProperty(Boolean.class, prop, area);
590         return handleNullAndPropertyStatus(carProp, area, false);
591     }
592 
593     /**
594      * Returns value of a float property
595      *
596      * <p> This method may take couple seconds to complete, so it needs to be called from an
597      * non-main thread.
598      *
599      * <p> This method has the same exception behavior as {@link #getBooleanProperty(int, int)}.
600      *
601      * @param prop Property ID to get
602      * @param area Area of the property to get
603      *
604      * @throws {@link CarInternalErrorException} when there is an error detected in cars.
605      * @throws {@link PropertyAccessDeniedSecurityException} when cars denied the access of the
606      * property.
607      * @throws {@link PropertyNotAvailableAndRetryException} when the property is temporarily
608      * not available and likely that retrying will be successful.
609      * @throws {@link PropertyNotAvailableException} when the property is temporarily not available.
610      * @throws {@link IllegalArgumentException} when the property in the areaId is not supplied.
611      *
612      * @return value of a float property, 0 if can not get value from the cars.
613      */
614     @AddedInOrBefore(majorVersion = 33)
getFloatProperty(int prop, int area)615     public float getFloatProperty(int prop, int area) {
616         checkSupportedProperty(prop);
617         CarPropertyValue<Float> carProp = getProperty(Float.class, prop, area);
618         return handleNullAndPropertyStatus(carProp, area, 0f);
619     }
620 
621     /**
622      * Returns value of an integer property
623      *
624      * <p> This method may take couple seconds to complete, so it needs to be called form an
625      * non-main thread.
626      *
627      * <p> This method has the same exception behavior as {@link #getBooleanProperty(int, int)}.
628      *
629      * @param prop Property ID to get
630      * @param area Zone of the property to get
631      *
632      * @throws {@link CarInternalErrorException} when there is an error detected in cars.
633      * @throws {@link PropertyAccessDeniedSecurityException} when cars denied the access of the
634      * property.
635      * @throws {@link PropertyNotAvailableAndRetryException} when the property is temporarily
636      * not available and likely that retrying will be successful.
637      * @throws {@link PropertyNotAvailableException} when the property is temporarily not available.
638      * @throws {@link IllegalArgumentException} when the property in the areaId is not supplied.
639      *
640      * @return value of an integer property, 0 if can not get the value from cars.
641      */
642     @AddedInOrBefore(majorVersion = 33)
getIntProperty(int prop, int area)643     public int getIntProperty(int prop, int area) {
644         checkSupportedProperty(prop);
645         CarPropertyValue<Integer> carProp = getProperty(Integer.class, prop, area);
646         return handleNullAndPropertyStatus(carProp, area, 0);
647     }
648 
649     /**
650      * Returns value of an integer array property
651      *
652      * <p> This method may take couple seconds to complete, so it needs to be called from an
653      * non-main thread.
654      *
655      * <p> This method has the same exception behavior as {@link #getBooleanProperty(int, int)}.
656      *
657      * @param prop Property ID to get
658      * @param area Zone of the property to get
659      *
660      * @throws {@link CarInternalErrorException} when there is an error detected in cars.
661      * @throws {@link PropertyAccessDeniedSecurityException} when cars denied the access of the
662      * property.
663      * @throws {@link PropertyNotAvailableAndRetryException} when the property is temporarily
664      * not available and likely that retrying will be successful.
665      * @throws {@link PropertyNotAvailableException} when the property is temporarily not available.
666      * @throws {@link IllegalArgumentException} when the property in the areaId is not supplied.
667      *
668      * @return value of an integer array property, an empty integer array if can not get the value
669      * from cars.
670      */
671     @NonNull
672     @AddedInOrBefore(majorVersion = 33)
getIntArrayProperty(int prop, int area)673     public int[] getIntArrayProperty(int prop, int area) {
674         checkSupportedProperty(prop);
675         CarPropertyValue<Integer[]> carProp = getProperty(Integer[].class, prop, area);
676         Integer[] res = handleNullAndPropertyStatus(carProp, area, new Integer[0]);
677         return toIntArray(res);
678     }
679 
toIntArray(Integer[] input)680     private static int[] toIntArray(Integer[] input) {
681         int len = input.length;
682         int[] arr = new int[len];
683         for (int i = 0; i < len; i++) {
684             arr[i] = input[i];
685         }
686         return arr;
687     }
688 
handleNullAndPropertyStatus(CarPropertyValue<T> propertyValue, int areaId, T defaultValue)689     private <T> T handleNullAndPropertyStatus(CarPropertyValue<T> propertyValue, int areaId,
690             T defaultValue) {
691 
692         if (propertyValue == null) {
693             return defaultValue;
694         }
695 
696         // Keeps the same behavior as android R.
697         if (mAppTargetSdk < Build.VERSION_CODES.S) {
698             return propertyValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE
699                     ? propertyValue.getValue() : defaultValue;
700         }
701 
702         // throws new exceptions in android S.
703         switch (propertyValue.getStatus()) {
704             case CarPropertyValue.STATUS_ERROR:
705                 throw new CarInternalErrorException(propertyValue.getPropertyId(), areaId);
706             case CarPropertyValue.STATUS_UNAVAILABLE:
707                 throw new PropertyNotAvailableException(propertyValue.getPropertyId(), areaId);
708             default:
709                 return propertyValue.getValue();
710         }
711     }
712 
runSyncOperation(Callable<V> c)713     private static <V> V runSyncOperation(Callable<V> c)
714             throws RemoteException, ServiceSpecificException {
715         int retryCount = 0;
716         while (retryCount < SYNC_OP_RETRY_MAX_COUNT) {
717             retryCount++;
718             try {
719                 return c.call();
720             } catch (ServiceSpecificException e) {
721                 if (e.errorCode != SYNC_OP_LIMIT_TRY_AGAIN) {
722                     throw e;
723                 }
724                 // If car service don't have enough binder thread to handle this request. Sleep for
725                 // 10ms and try again.
726                 Log.d(TAG, "too many sync request, sleeping for " + SYNC_OP_RETRY_SLEEP_IN_MS
727                         + " ms before retry");
728                 SystemClock.sleep(SYNC_OP_RETRY_SLEEP_IN_MS);
729                 continue;
730             } catch (RuntimeException | RemoteException e) {
731                 throw e;
732             } catch (Exception e) {
733                 Log.e(TAG, "catching unexpected exception for getProperty/setProperty", e);
734                 return null;
735             }
736         }
737         throw new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR,
738                 "failed to call car service sync operations after " + retryCount + " retries");
739     }
740 
741     /**
742      * Return CarPropertyValue
743      *
744      * <p> This method may take couple seconds to complete, so it needs to be called from an
745      * non-main thread.
746      *
747      * <p> Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} equal
748      * or later than {@link Build.VERSION_CODES#R} will receive the following exceptions when
749      * request is failed.
750      * <ul>
751      *     <li>{@link CarInternalErrorException}
752      *     <li>{@link PropertyAccessDeniedSecurityException}
753      *     <li>{@link PropertyNotAvailableAndRetryException}
754      *     <li>{@link PropertyNotAvailableException}
755      *     <li>{@link IllegalArgumentException}
756      * </ul>
757      * <p> Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion}
758      * earlier than {@link Build.VERSION_CODES#R} will receive the following exceptions when request
759      * is failed.
760      * <ul>
761      *     <li>{@link IllegalStateException} when there is an error detected in cars.
762      *     <li>{@link IllegalArgumentException} when the property in the areaId is not supplied.
763      * </ul>
764      *
765      * @param clazz The class object for the CarPropertyValue
766      * @param propId Property ID to get
767      * @param areaId Zone of the property to get
768      *
769      * @throws {@link CarInternalErrorException} when there is an error detected in cars.
770      * @throws {@link PropertyAccessDeniedSecurityException} when cars denied the access of the
771      * property.
772      * @throws {@link PropertyNotAvailableAndRetryException} when the property is temporarily
773      * not available and likely that retrying will be successful.
774      * @throws {@link PropertyNotAvailableException} when the property is temporarily not available.
775      * @throws {@link IllegalArgumentException} when the property in the areaId is not supplied.
776      *
777      * @return CarPropertyValue. Null if property's id is invalid.
778      */
779     @SuppressWarnings("unchecked")
780     @Nullable
781     @AddedInOrBefore(majorVersion = 33)
getProperty(@onNull Class<E> clazz, int propId, int areaId)782     public <E> CarPropertyValue<E> getProperty(@NonNull Class<E> clazz, int propId, int areaId) {
783         if (DBG) {
784             Log.d(TAG, "getProperty, propId: 0x" + toHexString(propId)
785                     + ", areaId: 0x" + toHexString(areaId) + ", class: " + clazz);
786         }
787 
788         checkSupportedProperty(propId);
789 
790         try {
791             CarPropertyValue<E> propVal = mService.getProperty(propId, areaId);
792             if (propVal != null && propVal.getValue() != null) {
793                 Class<?> actualClass = propVal.getValue().getClass();
794                 if (actualClass != clazz) {
795                     throw new IllegalArgumentException("Invalid property type. " + "Expected: "
796                             + clazz + ", but was: " + actualClass);
797                 }
798             }
799             return propVal;
800         } catch (RemoteException e) {
801             return handleRemoteExceptionFromCarService(e, null);
802         } catch (ServiceSpecificException e) {
803             // For pre R apps, throws the old exceptions.
804             if (mAppTargetSdk < Build.VERSION_CODES.R) {
805                 if (e.errorCode == VehicleHalStatusCode.STATUS_TRY_AGAIN) {
806                     return null;
807                 } else {
808                     throw new IllegalStateException(String.format("Failed to get property: 0x%x, "
809                             + "areaId: 0x%x", propId, areaId));
810                 }
811             }
812             return handleCarServiceSpecificException(e, propId, areaId, null);
813         }
814     }
815 
816     /**
817      * Query CarPropertyValue with property id and areaId.
818      *
819      * <p> This method may take couple seconds to complete, so it needs to be called from an
820      * non-main thread.
821      *
822      * <p> Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} equal
823      * or later than {@link Build.VERSION_CODES#R} will receive the following exceptions when
824      * request is failed.
825      * <ul>
826      *     <li>{@link CarInternalErrorException}
827      *     <li>{@link PropertyAccessDeniedSecurityException}
828      *     <li>{@link PropertyNotAvailableAndRetryException}
829      *     <li>{@link PropertyNotAvailableException}
830      *     <li>{@link IllegalArgumentException}
831      * </ul>
832      * <p> Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion}
833      * earlier than {@link Build.VERSION_CODES#R} will receive the following exceptions when request
834      * is failed.
835      * <ul>
836      *     <li>{@link IllegalStateException} when there is an error detected in cars.
837      *     <li>{@link IllegalArgumentException} when the property in the areaId is not supplied.
838      * </ul>
839      *
840      * @param propId Property Id
841      * @param areaId areaId
842      * @param <E> Value type of the property
843      *
844      * @throws {@link CarInternalErrorException} when there is an error detected in cars.
845      * @throws {@link PropertyAccessDeniedSecurityException} when cars denied the access of the
846      * property.
847      * @throws {@link PropertyNotAvailableAndRetryException} when the property is temporarily
848      * not available and likely that retrying will be successful.
849      * @throws {@link PropertyNotAvailableException} when the property is temporarily not available.
850      * @throws {@link IllegalArgumentException} when the property in the areaId is not supplied.
851      *
852      * @return CarPropertyValue. Null if property's id is invalid.
853      */
854     @Nullable
855     @AddedInOrBefore(majorVersion = 33)
getProperty(int propId, int areaId)856     public <E> CarPropertyValue<E> getProperty(int propId, int areaId) {
857         checkSupportedProperty(propId);
858 
859         try {
860             CarPropertyValue<E> propVal = runSyncOperation(() -> {
861                 return mService.getProperty(propId, areaId);
862             });
863             return propVal;
864         } catch (RemoteException e) {
865             return handleRemoteExceptionFromCarService(e, null);
866         } catch (ServiceSpecificException e) {
867             if (mAppTargetSdk < Build.VERSION_CODES.R) {
868                 if (e.errorCode == VehicleHalStatusCode.STATUS_TRY_AGAIN) {
869                     return null;
870                 } else {
871                     throw new IllegalStateException(String.format("Failed to get property: 0x%x, "
872                             + "areaId: 0x%x", propId, areaId));
873                 }
874             }
875             return handleCarServiceSpecificException(e, propId, areaId, null);
876         }
877     }
878 
879     /**
880      * Set value of car property by areaId.
881      *
882      * <p>If multiple clients set a property for the same area id simultaneously, which one takes
883      * precedence is undefined. Typically, the last set operation (in the order that they are issued
884      * to the car's ECU) overrides the previous set operations.
885      *
886      * <p> This method may take couple seconds to complete, so it needs to be called form an
887      * non-main thread.
888      *
889      * <p> Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} equal
890      * or later than {@link Build.VERSION_CODES#R} will receive the following exceptions when
891      * request is failed.
892      * <ul>
893      *     <li>{@link CarInternalErrorException}
894      *     <li>{@link PropertyAccessDeniedSecurityException}
895      *     <li>{@link PropertyNotAvailableAndRetryException}
896      *     <li>{@link PropertyNotAvailableException}
897      *     <li>{@link IllegalArgumentException}
898      * </ul>
899      * <p> Clients that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion}
900      * earlier than {@link Build.VERSION_CODES#R} will receive the following exceptions when request
901      * is failed.
902      * <ul>
903      *     <li>{@link RuntimeException} when the property is temporarily not available.
904      *     <li>{@link IllegalStateException} when there is an error detected in cars.
905      *     <li>{@link IllegalArgumentException} when the property in the areaId is not supplied
906      * </ul>
907      *
908      * @param clazz The class object for the CarPropertyValue
909      * @param propId Property ID
910      * @param areaId areaId
911      * @param val Value of CarPropertyValue
912      * @param <E> data type of the given property, for example property that was
913      * defined as {@code VEHICLE_VALUE_TYPE_INT32} in vehicle HAL could be accessed using
914      * {@code Integer.class}.
915      *
916      * @throws {@link CarInternalErrorException} when there is an error detected in cars.
917      * @throws {@link PropertyAccessDeniedSecurityException} when cars denied the access of the
918      * property.
919      * @throws {@link PropertyNotAvailableException} when the property is temporarily not available.
920      * @throws {@link PropertyNotAvailableAndRetryException} when the property is temporarily
921      * not available and likely that retrying will be successful.
922      * @throws {@link IllegalStateException} when get an unexpected error code.
923      * @throws {@link IllegalArgumentException} when the property in the areaId is not supplied.
924      */
925     @AddedInOrBefore(majorVersion = 33)
setProperty(@onNull Class<E> clazz, int propId, int areaId, @NonNull E val)926     public <E> void setProperty(@NonNull Class<E> clazz, int propId, int areaId, @NonNull E val) {
927         if (DBG) {
928             Log.d(TAG, "setProperty, propId: 0x" + toHexString(propId)
929                     + ", areaId: 0x" + toHexString(areaId) + ", class: " + clazz + ", val: " + val);
930         }
931         checkSupportedProperty(propId);
932         try {
933             runSyncOperation(() -> {
934                 mService.setProperty(new CarPropertyValue<>(propId, areaId, val),
935                         mCarPropertyEventToService);
936                 return null;
937             });
938         } catch (RemoteException e) {
939             handleRemoteExceptionFromCarService(e);
940             return;
941         } catch (ServiceSpecificException e) {
942             if (mAppTargetSdk < Build.VERSION_CODES.R) {
943                 if (e.errorCode == VehicleHalStatusCode.STATUS_TRY_AGAIN) {
944                     throw new RuntimeException(String.format("Failed to set property: 0x%x, "
945                             + "areaId: 0x%x", propId, areaId));
946                 } else {
947                     throw new IllegalStateException(String.format("Failed to set property: 0x%x, "
948                             + "areaId: 0x%x", propId, areaId));
949                 }
950             }
951             handleCarServiceSpecificException(e, propId, areaId, null);
952         }
953     }
954 
955     /**
956      * Modifies a property.  If the property modification doesn't occur, an error event shall be
957      * generated and propagated back to the application.
958      *
959      * <p> This method may take couple seconds to complete, so it needs to be called from an
960      * non-main thread.
961      *
962      * @param prop Property ID to modify
963      * @param areaId AreaId to apply the modification.
964      * @param val Value to set
965      */
966     @AddedInOrBefore(majorVersion = 33)
setBooleanProperty(int prop, int areaId, boolean val)967     public void setBooleanProperty(int prop, int areaId, boolean val) {
968         setProperty(Boolean.class, prop, areaId, val);
969     }
970 
971     /**
972      * Set float value of property
973      *
974      * <p> This method may take couple seconds to complete, so it needs to be called from an
975      * non-main thread.
976      *
977      * @param prop Property ID to modify
978      * @param areaId AreaId to apply the modification
979      * @param val Value to set
980      */
981     @AddedInOrBefore(majorVersion = 33)
setFloatProperty(int prop, int areaId, float val)982     public void setFloatProperty(int prop, int areaId, float val) {
983         setProperty(Float.class, prop, areaId, val);
984     }
985 
986     /**
987      * Set int value of property
988      *
989      * <p> This method may take couple seconds to complete, so it needs to be called from an
990      * non-main thread.
991      *
992      * @param prop Property ID to modify
993      * @param areaId AreaId to apply the modification
994      * @param val Value to set
995      */
996     @AddedInOrBefore(majorVersion = 33)
setIntProperty(int prop, int areaId, int val)997     public void setIntProperty(int prop, int areaId, int val) {
998         setProperty(Integer.class, prop, areaId, val);
999     }
1000 
1001     // Handles ServiceSpecificException in CarService for R and later version.
handleCarServiceSpecificException( ServiceSpecificException e, int propId, int areaId, T returnValue)1002     private <T> T handleCarServiceSpecificException(
1003             ServiceSpecificException e, int propId, int areaId, T returnValue) {
1004         // We are not passing the error message down, so log it here.
1005         Log.w(TAG, "received ServiceSpecificException: " + e);
1006         int errorCode = e.errorCode;
1007         switch (errorCode) {
1008             case VehicleHalStatusCode.STATUS_NOT_AVAILABLE:
1009                 throw new PropertyNotAvailableException(propId, areaId);
1010             case VehicleHalStatusCode.STATUS_TRY_AGAIN:
1011                 throw new PropertyNotAvailableAndRetryException(propId, areaId);
1012             case VehicleHalStatusCode.STATUS_ACCESS_DENIED:
1013                 throw new PropertyAccessDeniedSecurityException(propId, areaId);
1014             case VehicleHalStatusCode.STATUS_INTERNAL_ERROR:
1015                 throw new CarInternalErrorException(propId, areaId);
1016             default:
1017                 Log.e(TAG, "Invalid errorCode: " + errorCode + " in CarService");
1018         }
1019         return returnValue;
1020     }
1021 
1022     /**
1023      * Checks if the given property can be exposed to by this manager.
1024      *
1025      * <p>For example, properties related to user management should only be manipulated by
1026      * {@code UserHalService}.
1027      *
1028      * @param propId property to be checked
1029      *
1030      * @throws IllegalArgumentException if the property is not supported.
1031      */
checkSupportedProperty(int propId)1032     private void checkSupportedProperty(int propId) {
1033         switch (propId) {
1034             case VehiclePropertyIds.INITIAL_USER_INFO:
1035             case VehiclePropertyIds.SWITCH_USER:
1036             case VehiclePropertyIds.CREATE_USER:
1037             case VehiclePropertyIds.REMOVE_USER:
1038             case VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION:
1039                 throw new IllegalArgumentException("Unsupported property: "
1040                         + VehiclePropertyIds.toString(propId) + " (" + propId + ")");
1041         }
1042     }
1043 
1044     /** @hide */
1045     @Override
1046     @AddedInOrBefore(majorVersion = 33)
onCarDisconnected()1047     public void onCarDisconnected() {
1048         synchronized (mLock) {
1049             mPropertyIdToCarPropertyEventCallbackController.clear();
1050         }
1051     }
1052 }
1053