• 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 package com.android.car.hal;
17 
18 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
19 
20 import static java.lang.Integer.toHexString;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.car.VehiclePropertyIds;
25 import android.car.builtin.os.BuildHelper;
26 import android.car.builtin.util.Slogf;
27 import android.car.hardware.CarPropertyConfig;
28 import android.car.hardware.CarPropertyValue;
29 import android.car.hardware.property.CarPropertyEvent;
30 import android.car.hardware.property.CarPropertyManager;
31 import android.hardware.automotive.vehicle.VehiclePropError;
32 import android.hardware.automotive.vehicle.VehicleProperty;
33 import android.os.ServiceSpecificException;
34 import android.util.Pair;
35 import android.util.SparseArray;
36 
37 import com.android.car.CarLog;
38 import com.android.car.CarServiceUtils;
39 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
40 import com.android.internal.annotations.GuardedBy;
41 
42 import java.io.PrintWriter;
43 import java.util.ArrayList;
44 import java.util.Collection;
45 import java.util.HashSet;
46 import java.util.LinkedList;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.Set;
50 
51 /**
52  * Common interface for HAL services that send Vehicle Properties back and forth via ICarProperty.
53  * Services that communicate by passing vehicle properties back and forth via ICarProperty should
54  * extend this class.
55  */
56 public class PropertyHalService extends HalServiceBase {
57     private final boolean mDbg = true;
58     private final LinkedList<CarPropertyEvent> mEventsToDispatch = new LinkedList<>();
59     // Use SparseArray to save memory.
60     @GuardedBy("mLock")
61     private final SparseArray<CarPropertyConfig<?>> mMgrPropIdToCarPropConfig = new SparseArray<>();
62     @GuardedBy("mLock")
63     private final SparseArray<HalPropConfig> mHalPropIdToPropConfig =
64             new SparseArray<>();
65     @GuardedBy("mLock")
66     private final SparseArray<Pair<String, String>> mMgrPropIdToPermissions = new SparseArray<>();
67     // Only contains propId if the property Id is different in HAL and manager
68     private static final Map<Integer, Integer> PROPERTY_ID_HAL_TO_MANAGER = Map.of(
69             VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS,
70             VehiclePropertyIds.VEHICLE_SPEED_DISPLAY_UNITS);
71     // Only contains propId if the property Id is different in HAL and manager
72     private static final Map<Integer, Integer> PROPERTY_ID_MANAGER_TO_HAL = Map.of(
73             VehiclePropertyIds.VEHICLE_SPEED_DISPLAY_UNITS,
74             VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS);
75     private static final String TAG = CarLog.tagFor(PropertyHalService.class);
76     private final VehicleHal mVehicleHal;
77     private final PropertyHalServiceIds mPropIds;
78     private final HalPropValueBuilder mPropValueBuilder;
79 
80     @GuardedBy("mLock")
81     private PropertyHalListener mListener;
82     @GuardedBy("mLock")
83     private Set<Integer> mSubscribedHalPropIds;
84 
85     private final Object mLock = new Object();
86 
87     /**
88      * Converts manager property ID to Vehicle HAL property ID.
89      */
managerToHalPropId(int mgrPropId)90     private static int managerToHalPropId(int mgrPropId) {
91         return PROPERTY_ID_MANAGER_TO_HAL.getOrDefault(mgrPropId, mgrPropId);
92     }
93 
94     /**
95      * Converts Vehicle HAL property ID to manager property ID.
96      */
halToManagerPropId(int halPropId)97     private static int halToManagerPropId(int halPropId) {
98         return PROPERTY_ID_HAL_TO_MANAGER.getOrDefault(halPropId, halPropId);
99     }
100 
101     // Checks if the property exists in this VHAL before calling methods in IVehicle.
isPropertySupportedInVehicle(int halPropId)102     private boolean isPropertySupportedInVehicle(int halPropId) {
103         synchronized (mLock) {
104             return mHalPropIdToPropConfig.contains(halPropId);
105         }
106     }
107 
108     /**
109      * PropertyHalListener used to send events to CarPropertyService
110      */
111     public interface PropertyHalListener {
112         /**
113          * This event is sent whenever the property value is updated
114          * @param events
115          */
onPropertyChange(List<CarPropertyEvent> events)116         void onPropertyChange(List<CarPropertyEvent> events);
117         /**
118          * This event is sent when the set property call fails
119          * @param property
120          * @param area
121          */
onPropertySetError(int property, int area, @CarPropertyManager.CarSetPropertyErrorCode int errorCode)122         void onPropertySetError(int property, int area,
123                 @CarPropertyManager.CarSetPropertyErrorCode int errorCode);
124 
125     }
126 
PropertyHalService(VehicleHal vehicleHal)127     public PropertyHalService(VehicleHal vehicleHal) {
128         mPropIds = new PropertyHalServiceIds();
129         mSubscribedHalPropIds = new HashSet<Integer>();
130         mVehicleHal = vehicleHal;
131         if (mDbg) {
132             Slogf.d(TAG, "started PropertyHalService");
133         }
134         mPropValueBuilder = vehicleHal.getHalPropValueBuilder();
135     }
136 
137     /**
138      * Set the listener for the HAL service
139      * @param listener
140      */
setListener(PropertyHalListener listener)141     public void setListener(PropertyHalListener listener) {
142         synchronized (mLock) {
143             mListener = listener;
144         }
145     }
146 
147     /**
148      *
149      * @return SparseArray<CarPropertyConfig> List of configs available.
150      */
getPropertyList()151     public SparseArray<CarPropertyConfig<?>> getPropertyList() {
152         if (mDbg) {
153             Slogf.d(TAG, "getPropertyList");
154         }
155         synchronized (mLock) {
156             if (mMgrPropIdToCarPropConfig.size() == 0) {
157                 for (int i = 0; i < mHalPropIdToPropConfig.size(); i++) {
158                     HalPropConfig p = mHalPropIdToPropConfig.valueAt(i);
159                     int mgrPropId = halToManagerPropId(p.getPropId());
160                     CarPropertyConfig config = p.toCarPropertyConfig(mgrPropId);
161                     mMgrPropIdToCarPropConfig.put(mgrPropId, config);
162                 }
163             }
164             return mMgrPropIdToCarPropConfig;
165         }
166     }
167 
168     /**
169      * Returns property or null if property is not ready yet.
170      * @param mgrPropId property id in {@link VehiclePropertyIds}
171      * @param areaId area id
172      * @throws IllegalArgumentException if argument is not valid.
173      * @throws ServiceSpecificException if there is an exception in HAL.
174      */
175     @Nullable
getProperty(int mgrPropId, int areaId)176     public CarPropertyValue getProperty(int mgrPropId, int areaId)
177             throws IllegalArgumentException, ServiceSpecificException {
178         int halPropId = managerToHalPropId(mgrPropId);
179         if (!isPropertySupportedInVehicle(halPropId)) {
180             throw new IllegalArgumentException("Invalid property Id : 0x" + toHexString(mgrPropId));
181         }
182 
183         // CarPropertyManager catches and rethrows exception, no need to handle here.
184         HalPropValue value = mVehicleHal.get(halPropId, areaId);
185         if (value == null) {
186             return null;
187         }
188         HalPropConfig propConfig;
189         synchronized (mLock) {
190             propConfig = mHalPropIdToPropConfig.get(halPropId);
191         }
192         return value.toCarPropertyValue(mgrPropId, propConfig);
193     }
194 
195     /**
196      * Return property or null if property is not ready yet or there is an exception in HAL.
197      */
198     @Nullable
getPropertySafe(int mgrPropId, int areaId)199     public CarPropertyValue getPropertySafe(int mgrPropId, int areaId) {
200         try {
201             return getProperty(mgrPropId, areaId);
202         } catch (Exception e) {
203             Slogf.w(TAG, "get property value failed for property id: 0x "
204                     + toHexString(mgrPropId) + " area id: 0x" + toHexString(areaId)
205                     + " exception: " + e);
206             return null;
207         }
208     }
209 
210     /**
211      * Returns sample rate for the property
212      * @param mgrPropId
213      */
getSampleRate(int mgrPropId)214     public float getSampleRate(int mgrPropId) {
215         int halPropId = managerToHalPropId(mgrPropId);
216         if (!isPropertySupportedInVehicle(halPropId)) {
217             throw new IllegalArgumentException("Invalid property Id : 0x" + toHexString(mgrPropId));
218         }
219         return mVehicleHal.getSampleRate(halPropId);
220     }
221 
222     /**
223      * Get the read permission string for the property.
224      * @param mgrPropId
225      */
226     @Nullable
getReadPermission(int mgrPropId)227     public String getReadPermission(int mgrPropId) {
228         int halPropId = managerToHalPropId(mgrPropId);
229         return mPropIds.getReadPermission(halPropId);
230     }
231 
232     /**
233      * Get the write permission string for the property.
234      * @param mgrPropId
235      */
236     @Nullable
getWritePermission(int mgrPropId)237     public String getWritePermission(int mgrPropId) {
238         int halPropId = managerToHalPropId(mgrPropId);
239         return mPropIds.getWritePermission(halPropId);
240     }
241 
242     /**
243      * Get permissions for all properties in the vehicle.
244      * @return a SparseArray. key: propertyId, value: Pair(readPermission, writePermission).
245      */
246     @NonNull
getPermissionsForAllProperties()247     public SparseArray<Pair<String, String>> getPermissionsForAllProperties() {
248         synchronized (mLock) {
249             if (mMgrPropIdToPermissions.size() != 0) {
250                 return mMgrPropIdToPermissions;
251             }
252             for (int i = 0; i < mHalPropIdToPropConfig.size(); i++) {
253                 int halPropId = mHalPropIdToPropConfig.keyAt(i);
254                 mMgrPropIdToPermissions.put(halToManagerPropId(halPropId),
255                         new Pair<>(mPropIds.getReadPermission(halPropId),
256                                 mPropIds.getWritePermission(halPropId)));
257             }
258             return mMgrPropIdToPermissions;
259         }
260     }
261 
262     /**
263      * Return true if property is a display_units property
264      * @param mgrPropId
265      */
isDisplayUnitsProperty(int mgrPropId)266     public boolean isDisplayUnitsProperty(int mgrPropId) {
267         int halPropId = managerToHalPropId(mgrPropId);
268         return mPropIds.isPropertyToChangeUnits(halPropId);
269     }
270 
271     /**
272      * Set the property value.
273      * @param prop
274      *
275      * @throws IllegalArgumentException if argument is invalid.
276      * @throws ServiceSpecificException if there is an exception in HAL.
277      */
setProperty(CarPropertyValue prop)278     public void setProperty(CarPropertyValue prop)
279             throws IllegalArgumentException, ServiceSpecificException {
280         int halPropId = managerToHalPropId(prop.getPropertyId());
281         if (!isPropertySupportedInVehicle(halPropId)) {
282             throw new IllegalArgumentException("Invalid property Id : 0x"
283                     + toHexString(prop.getPropertyId()));
284         }
285         HalPropConfig propConfig;
286         synchronized (mLock) {
287             propConfig = mHalPropIdToPropConfig.get(halPropId);
288         }
289         HalPropValue halPropValue = mPropValueBuilder.build(prop, halPropId, propConfig);
290         // CarPropertyManager catches and rethrows exception, no need to handle here.
291         mVehicleHal.set(halPropValue);
292     }
293 
294     /**
295      * Subscribe to this property at the specified update rate.
296      * @param mgrPropId
297      * @param rate
298      *
299      * @throws IllegalArgumentException thrown if property is not supported by VHAL.
300      */
subscribeProperty(int mgrPropId, float rate)301     public void subscribeProperty(int mgrPropId, float rate) throws IllegalArgumentException {
302         if (mDbg) {
303             Slogf.d(TAG, "subscribeProperty propId=0x" + toHexString(mgrPropId) + ", rate=" + rate);
304         }
305         int halPropId = managerToHalPropId(mgrPropId);
306         if (!isPropertySupportedInVehicle(halPropId)) {
307             throw new IllegalArgumentException("Invalid property Id : 0x"
308                     + toHexString(mgrPropId));
309         }
310         synchronized (mLock) {
311             HalPropConfig cfg = mHalPropIdToPropConfig.get(halPropId);
312             if (rate > cfg.getMaxSampleRate()) {
313                 rate = cfg.getMaxSampleRate();
314             } else if (rate < cfg.getMinSampleRate()) {
315                 rate = cfg.getMinSampleRate();
316             }
317             mSubscribedHalPropIds.add(halPropId);
318         }
319 
320         mVehicleHal.subscribeProperty(this, halPropId, rate);
321     }
322 
323     /**
324      * Unsubscribe the property and turn off update events for it.
325      * @param mgrPropId
326      */
unsubscribeProperty(int mgrPropId)327     public void unsubscribeProperty(int mgrPropId) {
328         if (mDbg) {
329             Slogf.d(TAG, "unsubscribeProperty propId=0x" + toHexString(mgrPropId));
330         }
331         int halPropId = managerToHalPropId(mgrPropId);
332         if (!isPropertySupportedInVehicle(halPropId)) {
333             throw new IllegalArgumentException("Invalid property Id : 0x"
334                     + toHexString(mgrPropId));
335         }
336         synchronized (mLock) {
337             if (mSubscribedHalPropIds.contains(halPropId)) {
338                 mSubscribedHalPropIds.remove(halPropId);
339                 mVehicleHal.unsubscribeProperty(this, halPropId);
340             }
341         }
342     }
343 
344     @Override
init()345     public void init() {
346         if (mDbg) {
347             Slogf.d(TAG, "init()");
348         }
349     }
350 
351     @Override
release()352     public void release() {
353         if (mDbg) {
354             Slogf.d(TAG, "release()");
355         }
356         synchronized (mLock) {
357             for (Integer halProp : mSubscribedHalPropIds) {
358                 mVehicleHal.unsubscribeProperty(this, halProp);
359             }
360             mSubscribedHalPropIds.clear();
361             mHalPropIdToPropConfig.clear();
362             mMgrPropIdToCarPropConfig.clear();
363             mMgrPropIdToPermissions.clear();
364             mListener = null;
365         }
366     }
367 
368     @Override
isSupportedProperty(int propId)369     public boolean isSupportedProperty(int propId) {
370         return mPropIds.isSupportedProperty(propId);
371     }
372 
373     @Override
getAllSupportedProperties()374     public int[] getAllSupportedProperties() {
375         return CarServiceUtils.EMPTY_INT_ARRAY;
376     }
377 
378     // The method is called in HAL init(). Avoid handling complex things in here.
379     @Override
takeProperties(Collection<HalPropConfig> allProperties)380     public void takeProperties(Collection<HalPropConfig> allProperties) {
381         for (HalPropConfig p : allProperties) {
382             int propId = p.getPropId();
383             if (mPropIds.isSupportedProperty(propId)) {
384                 synchronized (mLock) {
385                     mHalPropIdToPropConfig.put(propId, p);
386                 }
387                 if (mDbg) {
388                     Slogf.d(TAG, "takeSupportedProperties: " + toHexString(propId));
389                 }
390             }
391         }
392         if (mDbg) {
393             Slogf.d(TAG, "takeSupportedProperties() took " + allProperties.size()
394                     + " properties");
395         }
396         // If vehicle hal support to select permission for vendor properties.
397         HalPropConfig customizePermission;
398         synchronized (mLock) {
399             customizePermission = mHalPropIdToPropConfig.get(
400                     VehicleProperty.SUPPORT_CUSTOMIZE_VENDOR_PERMISSION);
401         }
402         if (customizePermission != null) {
403             mPropIds.customizeVendorPermission(customizePermission.getConfigArray());
404         }
405     }
406 
407     @Override
onHalEvents(List<HalPropValue> values)408     public void onHalEvents(List<HalPropValue> values) {
409         PropertyHalListener listener;
410         synchronized (mLock) {
411             listener = mListener;
412         }
413         if (listener != null) {
414             for (HalPropValue v : values) {
415                 if (v == null) {
416                     continue;
417                 }
418                 int propId = v.getPropId();
419                 if (!isPropertySupportedInVehicle(propId)) {
420                     Slogf.w(TAG, "Property is not supported: 0x" + toHexString(propId));
421                     continue;
422                 }
423                 // Check payload if it is a userdebug build.
424                 if (BuildHelper.isDebuggableBuild() && !mPropIds.checkPayload(v)) {
425                     Slogf.w(TAG, "Drop event for property: " + v + " because it is failed "
426                             + "in payload checking.");
427                     continue;
428                 }
429                 int mgrPropId = halToManagerPropId(propId);
430                 HalPropConfig propConfig;
431                 synchronized (mLock) {
432                     propConfig = mHalPropIdToPropConfig.get(propId);
433                 }
434                 CarPropertyValue<?> propVal = v.toCarPropertyValue(mgrPropId, propConfig);
435                 CarPropertyEvent event = new CarPropertyEvent(
436                         CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, propVal);
437                 mEventsToDispatch.add(event);
438             }
439             listener.onPropertyChange(mEventsToDispatch);
440             mEventsToDispatch.clear();
441         }
442     }
443 
444     @Override
onPropertySetError(ArrayList<VehiclePropError> errors)445     public void onPropertySetError(ArrayList<VehiclePropError> errors) {
446         PropertyHalListener listener;
447         synchronized (mLock) {
448             listener = mListener;
449         }
450         if (listener != null) {
451             for (VehiclePropError error : errors) {
452                 int mgrPropId = halToManagerPropId(error.propId);
453                 listener.onPropertySetError(mgrPropId, error.areaId, error.errorCode);
454             }
455         }
456     }
457 
458     @Override
459     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(PrintWriter writer)460     public void dump(PrintWriter writer) {
461         writer.println(TAG);
462         writer.println("  Properties available:");
463         synchronized (mLock) {
464             for (int i = 0; i < mHalPropIdToPropConfig.size(); i++) {
465                 HalPropConfig p = mHalPropIdToPropConfig.valueAt(i);
466                 writer.println("    " + p.toString());
467             }
468         }
469     }
470 }
471