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